#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <uuid/uuid.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <sysexits.h>
#include <membershipPriv.h>
#include <grp.h>
#include <CoreFoundation/CoreFoundation.h>
#include <OpenDirectory/OpenDirectory.h>
#include <OpenDirectory/OpenDirectoryPriv.h>
#include "dstools_version.h"
static void printArray( const void *value, void *context )
{
CFStringRef cfPrintString = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("\t\t%@"), value );
CFShow( cfPrintString );
CFRelease( cfPrintString );
}
static void printDictionary( const void *key, const void *value, void *context )
{
CFStringRef cfPrintString = CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR("%@ -"), key );
CFShow( cfPrintString );
CFRelease( cfPrintString );
CFArrayApplyFunction( value, CFRangeMake(0, CFArrayGetCount(value)), printArray, NULL );
}
static SInt32 printErrorOrMessage( CFErrorRef *inError, const char *errorString, bool inVerbose )
{
SInt32 errorCode = EX_USAGE;
if ( inError == NULL || (*inError) == NULL || (inVerbose == false && errorString != NULL) )
{
CFStringRef cfString = CFStringCreateWithCString( kCFAllocatorDefault, errorString, kCFStringEncodingUTF8 );
if ( cfString != NULL ) {
CFShow( cfString );
CFRelease( cfString );
}
}
else if ( inError != NULL && (*inError) != NULL )
{
if ( inVerbose == true )
{
CFStringRef cfString = CFErrorCopyDescription( *inError );
if ( cfString != NULL ) {
CFShow( cfString );
CFRelease( cfString );
}
}
CFIndex errorCode = CFErrorGetCode( *inError );
if ( errorCode >= kODErrorCredentialsInvalid && errorCode < kODErrorCredentialsInvalid+999 ) {
errorCode = EX_NOPERM;
}
*inError = NULL;
}
return errorCode;
}
#pragma mark -
#pragma mark Text Input Routines
volatile int intr;
void
intcatch(int dontcare)
{
intr = 1;
}
static char *
read_passphrase(const char *prompt, int from_stdin)
{
char buf[1024], *p, ch;
struct termios tio, saved_tio;
sigset_t oset, nset;
struct sigaction sa, osa;
int input, output, echo = 0;
if (from_stdin) {
input = STDIN_FILENO;
output = STDERR_FILENO;
} else
input = output = open("/dev/tty", O_RDWR);
if (input == -1)
fprintf(stderr, "You have no controlling tty. Cannot read passphrase.\n");
sigemptyset(&nset);
sigaddset(&nset, SIGTSTP);
(void) sigprocmask(SIG_BLOCK, &nset, &oset);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = intcatch;
(void) sigaction(SIGINT, &sa, &osa);
intr = 0;
if (tcgetattr(input, &saved_tio) == 0 && (saved_tio.c_lflag & ECHO)) {
echo = 1;
tio = saved_tio;
tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
(void) tcsetattr(input, TCSANOW, &tio);
}
fflush(stdout);
(void)write(output, prompt, strlen(prompt));
for (p = buf; read(input, &ch, 1) == 1 && ch != '\n';) {
if (intr)
break;
if (p < buf + sizeof(buf) - 1)
*p++ = ch;
}
*p = '\0';
if (!intr)
(void)write(output, "\n", 1);
if (echo)
tcsetattr(input, TCSANOW, &saved_tio);
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
(void) sigaction(SIGINT, &osa, NULL);
if (intr) {
kill(getpid(), SIGINT);
sigemptyset(&nset);
sigsuspend(&nset);
}
if (!from_stdin)
(void)close(input);
p = (char *)malloc(strlen(buf)+1);
strcpy(p, buf);
memset(buf, 0, sizeof(buf));
return (p);
}
static bool isLegacyGroup( ODRecordRef inRecordRef, CFArrayRef* outShortNameMembers )
{
CFIndex shortNameMembersCount = 0;
CFIndex guidMembersCount = 0;
CFArrayRef shortNameMembers = ODRecordCopyValues( inRecordRef, kODAttributeTypeGroupMembership, NULL );
if ( shortNameMembers != NULL )
{
shortNameMembersCount = CFArrayGetCount( shortNameMembers );
if ( outShortNameMembers != NULL )
{
*outShortNameMembers = shortNameMembers;
CFRetain( shortNameMembers );
}
CFRelease( shortNameMembers );
}
CFArrayRef guidMembers = ODRecordCopyValues( inRecordRef, kODAttributeTypeGroupMembers, NULL );
if ( guidMembers != NULL ) {
guidMembersCount = CFArrayGetCount( guidMembers );
CFRelease( guidMembers );
}
return ( guidMembersCount == 0 && shortNameMembersCount > 0 );
}
void
usage(void)
{
printf("\ndseditgroup (%s):: Manipulate group records with the Open Directory API.\n\n", TOOLS_VERSION);
printf(
"Usage: dseditgroup [-pqv] -o edit [-n nodename] [-u username] [-P password]\n"
" [-r realname] [-c comment] [-s ttl] [-k keyword] [-i gid]\n"
" [-g uuid] [-S sid] [-a addmember] [-d deletemember] \n"
" [-t membertype] [-T grouptype] [-L] groupname\n"
"Usage: dseditgroup [-pqv] -o create [-n nodename] [-u username] [-P password]\n"
" [-r realname] [-c comment] [-s ttl] [-k keyword] [-i gid]\n"
" [-g uuid] [-S sid] [-T grouptype] [-L] groupname\n"
"Usage: dseditgroup [-pqv] -o delete [-u username] [-P password] [-T grouptype]\n"
" [-L] groupname\n"
"Usage: dseditgroup [-qv] -o read [-T grouptype] groupname\n"
"Usage: dseditgroup [-qv] -o checkmember [-m membername] groupname\n" );
printf("\nSee dseditgroup(8) man page for details.\n");
printf("\n");
}
int main(int argc, char *argv[])
{
int ch;
char *operation = nil;
bool bReadOption = false;
bool bCreateOption = false;
bool bDeleteOption = false;
bool bEditOption = false;
bool bInteractivePwd = false;
bool bNoVerify = false;
bool bVerbose = false;
bool bCheckMemberOption = false;
char *nodename = nil;
char *username = nil;
bool bDefaultUser = false;
bool bCompList = false;
char *password = nil;
char *addrecordname = nil;
char *delrecordname = nil;
char *recordtype = nil;
char *gid = nil;
char *guid = nil;
char *smbSID = nil;
char *realname = nil;
char *keyword = nil;
char *comment = nil;
char *timeToLive = nil;
char *groupname = nil;
char *member = nil;
char *format = nil; const char *grouptype = NULL;
int exitcode = 0;
uuid_t uuid;
ODNodeRef aDSNodeRef = NULL;
ODNodeRef aDSSearchRef = NULL;
bool bContinueAdd = false;
char *groupRecordName = nil;
__block ODRecordRef aGroupRecRef = NULL;
__block ODRecordRef aGroupRecRef2 = NULL;
CFErrorRef aErrorRef = NULL;
char *errorTok = NULL;
if (argc < 2)
{
usage();
exit(0);
}
if ( strcmp(argv[1], "-appleversion") == 0 )
dsToolAppleVersionExit( argv[0] );
while ((ch = getopt(argc, argv, "LT:o:pqvn:m:u:P:a:d:t:i:g:r:k:c:s:S:f:?h")) != -1) {
switch (ch) {
case 'o':
operation = strdup(optarg);
if (operation != nil)
{
if ( strcasecmp(operation, "read") == 0 )
{
bReadOption = true;
}
else if ( strcasecmp(operation, "create") == 0 )
{
bCreateOption = true;
}
else if ( strcasecmp(operation, "delete") == 0 )
{
bDeleteOption = true;
}
else if ( strcasecmp(operation, "edit") == 0 )
{
bEditOption = true;
}
else if ( strcasecmp(operation, "checkmember") == 0 )
{
bCheckMemberOption = true;
}
}
break;
case 'p':
bInteractivePwd = true;
break;
case 'q':
bNoVerify = true;
break;
case 'v':
bVerbose = true;
break;
case 'm':
member = strdup(optarg);
break;
case 'n':
nodename = strdup(optarg);
break;
case 'u':
username = strdup(optarg);
break;
case 'P':
password = strdup(optarg);
break;
case 'a':
addrecordname = strdup(optarg);
break;
case 'd':
delrecordname = strdup(optarg);
break;
case 't':
recordtype = strdup(optarg);
break;
case 'T':
grouptype = optarg;
break;
case 'L':
bCompList = true;
break;
case 'i':
strtol( optarg, &errorTok, 10 );
if ( errorTok == NULL || errorTok[0] == '\0' ) {
gid = strdup(optarg);
}
else {
printf( "GID contains non-numeric characters\n" );
return EX_USAGE;
}
break;
case 'g':
uuid_clear( uuid );
if ( uuid_parse(optarg, uuid) == 0 && uuid_is_null(uuid) == false ) {
guid = strdup(optarg);
}
else {
printf( "GUID provided is not a valid UUID\n" );
return EX_USAGE;
}
break;
case 'r':
realname = strdup(optarg);
break;
case 'k':
keyword = strdup(optarg);
break;
case 'c':
comment = strdup(optarg);
break;
case 's':
timeToLive = strdup(optarg);
break;
case 'S':
smbSID = strdup(optarg);
break;
case 'f':
format = strdup(optarg);
break;
case '?':
case 'h':
default:
{
usage();
return EX_USAGE;
}
}
}
argc -= optind;
argv += optind;
if (argc == 0)
{
printErrorOrMessage( NULL, "No group name provided", bVerbose );
return EX_USAGE;
}
groupname = strdup( argv[0] );
if (!bCreateOption && !bDeleteOption && !bEditOption && !bCheckMemberOption)
{
bReadOption = true; }
if (username == nil)
{
struct passwd* pw = NULL;
pw = getpwuid(getuid());
if (pw != NULL && pw->pw_name != NULL && pw->pw_name[0] != '\0')
{
username = strdup(pw->pw_name);
}
else
{
printf("***Username <-u username> must be explicitly provided in this shell***\n");
usage();
exit(0);
}
bDefaultUser = true;
}
if (bVerbose)
{
printf("dseditgroup verbose mode\n");
printf("Options selected by user:\n");
if (bReadOption)
printf("Read option selected\n");
if (bCreateOption)
printf("Create option selected\n");
if (bDeleteOption)
printf("Delete option selected\n");
if (bEditOption)
printf("Edit option selected\n");
if (bCheckMemberOption)
printf("Checking membership selected\n");
if (bInteractivePwd)
printf("Interactive password option selected\n");
if (bNoVerify)
printf("User verification is disabled\n");
if (nodename)
printf("Nodename provided as <%s>\n", nodename);
if (username && !bDefaultUser)
printf("Username provided as <%s>\n", username);
else
printf("Username determined to be <%s>\n", username);
if ( password && !bInteractivePwd )
printf("Password provided as <%s>\n", password);
if (addrecordname)
printf("Recordname to be added provided as <%s>\n", addrecordname);
if (delrecordname)
printf("Recordname to be deleted provided as <%s>\n", delrecordname);
if (recordtype)
printf("Recordtype provided as <%s>\n", recordtype);
if (grouptype)
printf("Grouptype provided as <%s>\n", grouptype);
if (gid)
printf("GID provided as <%s>\n", gid);
if (guid)
printf("GUID provided as <%s>\n", guid);
if (smbSID)
printf("SID provided as <%s>\n", smbSID);
if (realname)
printf("Realname provided as <%s>\n", realname);
if (keyword)
printf("Keyword provided as <%s>\n", keyword);
if (comment)
printf("Comment provided as <%s>\n", comment);
if (timeToLive)
printf("TimeToLive provided as <%s>\n", timeToLive);
if (groupname)
printf("Groupname provided as <%s>\n", groupname);
if (bCompList)
printf("Will maintain computer lists when applicable\n" );
printf("\n");
}
ODRecordType (^mapRecTypeWithDefault)(const char *, ODRecordType) = ^(const char *inType, ODRecordType inDefault) {
if ( inType != NULL )
{
if ( strcasecmp(inType, "user") == 0) {
return kODRecordTypeUsers;
}
else if ( strcasecmp(inType, "group") == 0) {
return kODRecordTypeGroups;
}
else if ( strcasecmp(inType, "computer") == 0) {
return kODRecordTypeComputers;
}
else if ( strcasecmp(inType, "computergroup") == 0 ) {
return kODRecordTypeComputerGroups;
}
}
return inDefault;
};
if (bCheckMemberOption == false &&
bReadOption == false &&
(!bNoVerify && ( !bDefaultUser && ( (password == nil) || bInteractivePwd ) ) || (bDefaultUser && bInteractivePwd)) )
{
password = read_passphrase("Please enter user password:", 1);
}
do {
bool bIsLocalNode = false;
ODNodeRef aLocalNodeRef = ODNodeCreateWithNodeType( kCFAllocatorDefault, kODSessionDefault, kODNodeTypeLocalNodes, &aErrorRef );
if (nodename == NULL)
{
if ( bEditOption == true || bReadOption == true || bCheckMemberOption == true || bDeleteOption == true ) {
aDSNodeRef = ODNodeCreateWithNodeType( kCFAllocatorDefault, kODSessionDefault, kODNodeTypeAuthentication, &aErrorRef );
}
else {
aDSNodeRef = aLocalNodeRef;
bIsLocalNode = true;
}
}
else if ( nodename[0] == '.' )
{
aDSNodeRef = aLocalNodeRef;
bIsLocalNode = true;
}
else
{
CFStringRef cfNodeName = CFStringCreateWithCString( kCFAllocatorDefault, nodename, kCFStringEncodingUTF8 );
if ( cfNodeName != NULL ) {
aDSNodeRef = ODNodeCreateWithName( kCFAllocatorDefault, kODSessionDefault, cfNodeName, &aErrorRef );
CFRelease( cfNodeName );
if (aDSNodeRef == NULL) {
exitcode = printErrorOrMessage(NULL, "Error locating specified node.", bVerbose);
break;
}
if ( CFEqual(ODNodeGetName(aDSNodeRef), ODNodeGetName(aLocalNodeRef)) == true ) {
bIsLocalNode = true;
}
}
else {
exitcode = printErrorOrMessage( NULL, "Error parsing node name.", bVerbose );
break;
}
}
if ( aDSNodeRef == NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "getNodeRef failed to obtain a node reference", bVerbose );
break;
}
aDSSearchRef = ODNodeCreateWithNodeType( kCFAllocatorDefault, kODSessionDefault, kODNodeTypeAuthentication, &aErrorRef );
CFStringRef groupNameCF = CFStringCreateWithCString( kCFAllocatorDefault, groupname, kCFStringEncodingUTF8 );
if ( groupNameCF == NULL ) {
exitcode = EX_SOFTWARE;
printErrorOrMessage( NULL, "Unable to parse groupname", bVerbose );
break;
}
CFArrayRef attribs = CFArrayCreate( kCFAllocatorDefault, (CFTypeRef *) &kODAttributeTypeStandardOnly, 1, &kCFTypeArrayCallBacks );
if ( attribs == NULL ) {
exitcode = EX_SOFTWARE;
printErrorOrMessage( NULL, "Unable to allocate array", bVerbose );
break;
}
ODRecordType grpType = mapRecTypeWithDefault( grouptype, kODRecordTypeGroups );
bool (^isLocalNode)(ODRecordRef record) = ^(ODRecordRef record) {
CFArrayRef values = ODRecordCopyValues( record, kODAttributeTypeMetaNodeLocation, NULL );
if ( values != NULL ) {
if ( CFArrayGetCount(values) > 0 && CFEqual(CFArrayGetValueAtIndex(values, 0), ODNodeGetName(aLocalNodeRef)) == true ) {
return (bool) true;
}
CFRelease( values );
}
return (bool) false;
};
aGroupRecRef = ODNodeCopyRecord( aDSNodeRef, grpType, groupNameCF, attribs, NULL );
if ( aGroupRecRef != NULL ) {
bIsLocalNode = isLocalNode( aGroupRecRef );
}
if (aGroupRecRef == NULL && !bCreateOption) {
exitcode = printErrorOrMessage(NULL, "Group not found.", bVerbose);
break;
}
if ( bCompList == true && grpType == kODRecordTypeComputerGroups )
{
aGroupRecRef2 = ODNodeCopyRecord( aDSNodeRef, kODRecordTypeComputerLists, groupNameCF, NULL, NULL );
if ( aGroupRecRef2 != NULL && isLocalNode(aGroupRecRef2) == true )
{
if ( bIsLocalNode == false && aGroupRecRef != NULL )
{
if ( bVerbose == true ) {
printf( "Skipping Computer list because it's on a different node\n" );
CFRelease( aGroupRecRef2 );
aGroupRecRef2 = NULL;
}
}
else {
bIsLocalNode = true;
}
}
}
if ( geteuid() == 0 && bIsLocalNode == true && (username == NULL || password == NULL) )
{
if ( bVerbose == true ) {
printf( "Skipping authentication because user has effective ID 0\n" );
}
}
else if ( bDeleteOption == true || bCreateOption == true || bEditOption == true )
{
if (username == NULL || password == NULL) {
exitcode = printErrorOrMessage(NULL, "Username and password must be provided.", bVerbose);
break;
}
bool bSuccess = false;
CFStringRef user = CFStringCreateWithCString(NULL, username, kCFStringEncodingUTF8);
CFStringRef pass = CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8);
if (aGroupRecRef != NULL) {
bSuccess = ODRecordSetNodeCredentials(aGroupRecRef, user, pass, &aErrorRef);
if (aGroupRecRef2 != NULL) {
ODRecordSetNodeCredentials(aGroupRecRef2, user, pass, &aErrorRef);
}
} else {
bSuccess = ODNodeSetCredentials(aDSNodeRef, NULL, user, pass, &aErrorRef);
}
CFRelease(user);
CFRelease(pass);
if (!bSuccess) {
exitcode = printErrorOrMessage(&aErrorRef, "Failed to set credentials.", bVerbose);
break;
}
}
CFErrorRef (^deleteRecords)(void) = ^(void) {
CFErrorRef error = NULL;
if ( aGroupRecRef != NULL )
{
if ( ODRecordDelete(aGroupRecRef, &error) == false ) {
return error;
}
CFRelease( aGroupRecRef );
aGroupRecRef = NULL;
}
if ( aGroupRecRef2 != NULL )
{
if ( ODRecordDelete(aGroupRecRef2, &error) == false ) {
return error;
}
CFRelease( aGroupRecRef2 );
aGroupRecRef2 = NULL;
}
return error;
};
if ( bReadOption == true || bDeleteOption == true )
{
if ( aGroupRecRef != NULL || aGroupRecRef2 != NULL )
{
if ( bDeleteOption == true && aGroupRecRef != NULL ) {
printErrorOrMessage( NULL, "Group record below will be deleted:", bVerbose );
}
CFDictionaryRef cfDetails = ODRecordCopyDetails( aGroupRecRef, NULL, NULL );
if ( cfDetails != NULL ) {
CFDictionaryApplyFunction( cfDetails, printDictionary, NULL );
CFRelease( cfDetails );
}
if ( bDeleteOption == true && (aErrorRef = deleteRecords()) != NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Unable to delete record", bVerbose );
break;
}
}
else
{
exitcode = printErrorOrMessage( NULL, "Group was not found.", bVerbose );
break;
}
}
else if (bCreateOption)
{
if ( aGroupRecRef != NULL || aGroupRecRef2 != NULL )
{
char responseValue[8] = {0};
if (!bNoVerify)
{
printf("Create called on existing record - do you want to overwrite, y or n : ");
scanf( "%c", responseValue );
printf("\n");
}
if (bNoVerify || (responseValue[0] == 'y') || (responseValue[0] == 'Y'))
{
if ( (aErrorRef = deleteRecords()) != NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Unable to replace the record", bVerbose );
break;
}
}
else
{
exitcode = EX_CANTCREAT;
printErrorOrMessage( NULL, "Operation cancelled because record could not be replaced", bVerbose );
break;
}
}
if ( aGroupRecRef == NULL )
{
aGroupRecRef = ODNodeCreateRecord( aDSNodeRef, grpType, groupNameCF, NULL, &aErrorRef );
if ( aGroupRecRef != NULL )
{
groupRecordName = strdup(groupname);
bContinueAdd = true;
if ( bCompList == true && grpType == kODRecordTypeComputerGroups ) {
aGroupRecRef2 = ODNodeCreateRecord( aDSNodeRef, kODRecordTypeComputerLists, groupNameCF, NULL, NULL );
}
}
else
{
exitcode = printErrorOrMessage( &aErrorRef, "Unable to create the record", bVerbose );
break;
}
}
}
else if (bEditOption)
{
if ( aGroupRecRef != NULL ) {
bContinueAdd = true;
}
else {
printErrorOrMessage( NULL, "Record not found", bVerbose );
break;
}
}
else if (bCheckMemberOption)
{
if ( aGroupRecRef != NULL )
{
const char *user = (member ? : username);
CFStringRef memberCF = CFStringCreateWithCString( kCFAllocatorDefault, user, kCFStringEncodingUTF8 );
if ( memberCF == NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Unable to to allocate string", bVerbose );
break;
}
ODRecordRef memberRec = ODNodeCopyRecord( aDSSearchRef, kODRecordTypeUsers, memberCF, NULL, &aErrorRef );
if ( memberRec == NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Unable to find the user record", bVerbose );
break;
}
if ( ODRecordContainsMember(aGroupRecRef, memberRec, &aErrorRef) == true ) {
printf("yes %s is a member of %s\n", user, groupname);
exitcode = EX_OK;
}
else {
printf("no %s is NOT a member of %s\n", user, groupname);
exitcode = EX_NOUSER;
}
CFRelease( memberRec );
CFRelease( memberCF );
}
else
{
exitcode = printErrorOrMessage( NULL, "Invalid group name", bVerbose );
break;
}
}
if ( (bCreateOption || bEditOption) && aGroupRecRef != NULL )
{
if (bContinueAdd)
{
ODRecordType recType = mapRecTypeWithDefault( recordtype, kODRecordTypeUsers );
if (format != nil)
{
CFArrayRef shortNameMembers = NULL;
bool isLegacy = isLegacyGroup( aGroupRecRef, &shortNameMembers );
if ( format[0] == 'n' && isLegacy == true )
{
CFMutableArrayRef newList = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
CFIndex count = CFArrayGetCount( shortNameMembers );
CFIndex ii;
for ( ii = 0; ii < count; ii++ )
{
char tmpStr[512]; CFStringRef recName = (CFStringRef) CFArrayGetValueAtIndex( shortNameMembers, ii );
const char *recNameStr = CFStringGetCStringPtr( recName, kCFStringEncodingUTF8 );
if ( recNameStr == NULL ) {
if ( CFStringGetCString(recName, tmpStr, sizeof(tmpStr), kCFStringEncodingUTF8) == false ) {
printErrorOrMessage( NULL, "Record name is more than 512 characers - something is wrong", bVerbose );
return EX_SOFTWARE;
}
recNameStr = tmpStr;
}
uuid_t uu;
if ( mbr_user_name_to_uuid(recNameStr, uu) == 0 ) {
uuid_string_t uuidStr;
uuid_unparse_upper( uu, uuidStr );
CFStringRef uuidCF = CFStringCreateWithCString( kCFAllocatorDefault, uuidStr, kCFStringEncodingUTF8 );
CFArrayAppendValue( newList, uuidCF );
CFRelease( uuidCF );
}
else {
int len = strlen( recNameStr );
char tempBuffer[sizeof("UUID for '%s' could not be found please remove bad entry before conversion") + len + 1];
snprintf( tempBuffer, len, "UUID for '%s' could not be found please remove bad entry before conversion", recNameStr );
printErrorOrMessage( NULL, tempBuffer, bVerbose );
return EX_TEMPFAIL;
}
}
if ( CFArrayGetCount(newList) != 0 )
{
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeGroupMembers, newList, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Failed to set the UUID based membership", bVerbose );
break;
}
}
}
else if ( format[0] == 'l' && isLegacy == false )
{
CFArrayRef newValue = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeGroupMembers, newValue, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Error removing GUIDs from the group", bVerbose );
break;
}
}
else
{
if ( bVerbose == true ) {
printErrorOrMessage( NULL, "Group is already in that format", bVerbose );
}
break;
}
}
if ( addrecordname != NULL )
{
CFStringRef memberName = CFStringCreateWithCString( kCFAllocatorDefault, addrecordname, kCFStringEncodingUTF8 );
if ( memberName != NULL ) {
ODRecordRef memberRef = ODNodeCopyRecord( aDSSearchRef, recType, memberName, NULL, &aErrorRef );
if ( memberRef != NULL ) {
if ( ODRecordAddMember(aGroupRecRef, memberRef, &aErrorRef) == false ) {
exitcode = printErrorOrMessage( &aErrorRef, "Could not add member to group.", bVerbose );
break;
}
ODRecordAddMember( aGroupRecRef2, memberRef, NULL );
}
else {
printErrorOrMessage( &aErrorRef, "Record was not found.", bVerbose );
exitcode = eDSRecordNotFound;
break;
}
}
else {
exitcode = eDSInvalidRecordName;
break;
}
}
if ( delrecordname != NULL )
{
CFStringRef memberName = CFStringCreateWithCString( kCFAllocatorDefault, delrecordname, kCFStringEncodingUTF8 );
if ( memberName != NULL ) {
ODRecordRef memberRef = ODNodeCopyRecord( aDSSearchRef, recType, memberName, NULL, &aErrorRef );
if ( memberRef != NULL ) {
if ( ODRecordRemoveMember(aGroupRecRef, memberRef, &aErrorRef) == false ) {
exitcode = printErrorOrMessage( &aErrorRef, "Could not remove member from group.", bVerbose );
break;
}
ODRecordRemoveMember( aGroupRecRef2, memberRef, NULL );
}
else {
printErrorOrMessage( &aErrorRef, "Record was not found.", bVerbose );
exitcode = eDSRecordNotFound;
break;
}
}
else {
exitcode = eDSInvalidRecordName;
break;
}
}
if ( gid != NULL )
{
CFStringRef gidCF = CFStringCreateWithCString( kCFAllocatorDefault, gid, kCFStringEncodingUTF8 );
if ( gidCF == NULL ) {
exitcode = printErrorOrMessage( NULL, "GID is invalid", bVerbose );
break;
}
ODQueryRef query = ODQueryCreateWithNodeType( kCFAllocatorDefault, kODNodeTypeAuthentication, kODRecordTypeGroups,
kODAttributeTypePrimaryGroupID, kODMatchEqualTo, gidCF, NULL, 1, &aErrorRef );
if ( query == NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Error searching for conflicting GID", bVerbose );
break;
}
CFArrayRef matches = ODQueryCopyResults( query, false, &aErrorRef );
if ( (matches == NULL || CFArrayGetCount(matches) == 0) && aErrorRef == NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypePrimaryGroupID, gidCF, &aErrorRef) == false ) {
exitcode = printErrorOrMessage( &aErrorRef, "Error attempting to set GID attribute", bVerbose );
break;
}
else {
if ( bVerbose == true ) {
printErrorOrMessage( &aErrorRef, "GID attribute was set", bVerbose );
}
exitcode = EX_OK;
}
}
else if ( aErrorRef != NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Error checking if GID exists", bVerbose );
break;
}
else {
exitcode = printErrorOrMessage( NULL, "GID already exists", bVerbose );
break;
}
CFRelease( gidCF );
}
else if ( bCreateOption ) {
gid_t newGID;
for ( newGID = 501; newGID < GID_MAX; newGID++ ) {
if ( getgrgid(newGID) == NULL ) {
gid = (char *) malloc( 32 );
snprintf( gid, 32, "%d", newGID );
break;
}
}
if ( gid != NULL )
{
bool bGIDIsOk = false;
CFStringRef gidCF = CFStringCreateWithCString( kCFAllocatorDefault, gid, kCFStringEncodingUTF8 );
if ( gidCF == NULL ) {
exitcode = printErrorOrMessage( NULL, "Unable to allocate string for PrimaryGroupID", bVerbose );
break;
}
ODQueryRef query = ODQueryCreateWithNodeType( kCFAllocatorDefault, kODNodeTypeAuthentication, kODRecordTypeGroups,
kODAttributeTypePrimaryGroupID, kODMatchEqualTo, gidCF, NULL, 1, &aErrorRef );
if ( query == NULL ) {
exitcode = printErrorOrMessage( &aErrorRef, "Unable to create query for PrimaryGroupID", bVerbose );
break;
}
CFArrayRef results = ODQueryCopyResults( query, false, &aErrorRef );
if ( results != NULL ) {
CFRelease( results );
bGIDIsOk = true;
}
CFRelease( query );
if ( bGIDIsOk == true && ODRecordSetValue(aGroupRecRef, kODAttributeTypePrimaryGroupID, gidCF, &aErrorRef) == false ) {
exitcode = printErrorOrMessage( &aErrorRef, "Unable to create query for PrimaryGroupID", bVerbose );
}
CFRelease( gidCF );
if ( bGIDIsOk == false ) {
exitcode = printErrorOrMessage( &aErrorRef, "Query for PrimaryGroupID failed", bVerbose );
break;
}
}
}
if ( guid != NULL )
{
uuid_string_t uuidStr;
uuid_unparse_upper( uuid, uuidStr );
CFStringRef cfUUID = CFStringCreateWithCString( kCFAllocatorDefault, uuidStr, kCFStringEncodingUTF8 );
if ( cfUUID != NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeGUID, cfUUID, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Add GUID value - FAILED", bVerbose );
break;
} else if ( bVerbose ) {
printErrorOrMessage( NULL, "Add GUID value - SUCCESS", bVerbose );
}
}
}
if ( smbSID )
{
CFStringRef newValue = CFStringCreateWithCString( kCFAllocatorDefault, smbSID, kCFStringEncodingUTF8 );
if ( newValue != NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeSMBSID, newValue, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Add SID value - FAILED", bVerbose );
} else if ( bVerbose ) {
printErrorOrMessage( NULL, "Add SID value - SUCCESS", bVerbose );
}
CFRelease( newValue );
}
}
if ( realname )
{
CFStringRef newValue = CFStringCreateWithCString( kCFAllocatorDefault, realname, kCFStringEncodingUTF8 );
if ( newValue != NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeFullName, newValue, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Add full name value - FAILED", bVerbose );
} else if ( bVerbose ) {
printErrorOrMessage( NULL, "Add full name value - SUCCESS", bVerbose );
}
ODRecordSetValue( aGroupRecRef2, kODAttributeTypeFullName, newValue, NULL );
CFRelease( newValue );
}
}
if ( keyword )
{
CFStringRef newValue = CFStringCreateWithCString( kCFAllocatorDefault, keyword, kCFStringEncodingUTF8 );
if ( newValue != NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeKeywords, newValue, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Add Keyword value - FAILED", bVerbose );
} else if ( bVerbose ) {
printErrorOrMessage( NULL, "Add Keyword value - SUCCESS", bVerbose );
}
ODRecordSetValue( aGroupRecRef2, kODAttributeTypeKeywords, newValue, NULL );
CFRelease( newValue );
}
}
if ( comment )
{
CFStringRef newValue = CFStringCreateWithCString( kCFAllocatorDefault, comment, kCFStringEncodingUTF8 );
if ( newValue != NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeComment, newValue, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Add Comment value - FAILED", bVerbose );
} else if ( bVerbose ) {
printErrorOrMessage( NULL, "Add Comment value - SUCCESS", bVerbose );
}
ODRecordSetValue( aGroupRecRef2, kODAttributeTypeComment, newValue, NULL );
CFRelease( newValue );
}
}
if ( timeToLive )
{
CFStringRef newValue = CFStringCreateWithCString( kCFAllocatorDefault, timeToLive, kCFStringEncodingUTF8 );
if ( newValue != NULL ) {
if ( ODRecordSetValue(aGroupRecRef, kODAttributeTypeTimeToLive, newValue, &aErrorRef) == false ) {
printErrorOrMessage( &aErrorRef, "Add TTL value - FAILED", bVerbose );
} else if ( bVerbose ) {
printErrorOrMessage( NULL, "Add TTL value - SUCCESS", bVerbose );
}
ODRecordSetValue( aGroupRecRef2, kODAttributeTypeTimeToLive, newValue, NULL );
CFRelease( newValue );
}
}
}
if ( bVerbose == true && (bCreateOption == true || bEditOption == true) )
{
if ( bCreateOption == true )
printErrorOrMessage( NULL, "\nGroup record created.\n", bVerbose );
if ( bEditOption == true )
printErrorOrMessage( NULL, "\nGroup record edited.\n", bVerbose );
printErrorOrMessage( NULL, "=======================\n", bVerbose );
ODRecordSynchronize( aGroupRecRef, NULL );
ODRecordSynchronize( aGroupRecRef2, NULL );
CFDictionaryRef cfDetails = ODRecordCopyDetails( aGroupRecRef, NULL, NULL );
if ( cfDetails != NULL ) {
CFDictionaryApplyFunction( cfDetails, printDictionary, NULL );
CFRelease( cfDetails );
}
}
}
break;
} while(true);
if ( aDSNodeRef != NULL )
{
CFRelease( aDSNodeRef );
aDSNodeRef = NULL;
}
if (nodename)
free(nodename);
if (username)
free(username);
if (password)
free(password);
if (addrecordname)
free(addrecordname);
if (delrecordname)
free(delrecordname);
if (recordtype)
free(recordtype);
if (gid)
free(gid);
if (guid)
free(guid);
if (realname)
free(realname);
if (keyword)
free(keyword);
if (comment)
free(comment);
if (timeToLive)
free(timeToLive);
if (groupname)
free(groupname);
if (groupRecordName)
free(groupRecordName);
return exitcode;
}