/* File: Main.cpp Product: NeST (NetInfo Setup Tool) Version: 1.1 Copyright: � 2000-2001 by Apple Computer, Inc., all rights reserved. */ #include <sys/types.h> #include <sys/uio.h> #include <pwd.h> #include <signal.h> // for signal handling #include <string.h> // for memset #include <stdlib.h> // for exit #include <openssl/rc4.h> #include <openssl/md5.h> #include <sysexits.h> #include <stdio.h> #include <stdint.h> #include <paths.h> #include "Main.h" #include "NetworkUtilities.h" #include "WatchdogSupport.h" #include <Foundation/Foundation.h> #include <PasswordServer/AuthDBFile.h> #include <PasswordServer/CPSUtilities.h> #include <PasswordServer/PasswordServerPrefsDefs.h> #include <SystemConfiguration/SystemConfiguration.h> #include <time.h> #include <unistd.h> #include <regex.h> #include <arpa/inet.h> #include <netinet/in.h> #include <sys/file.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/sysctl.h> // for struct kinfo_proc and sysctl() #include <sys/types.h> #include <Security/Authorization.h> #include "bridge.h" #include "KerberosInterface.h" #define kMaxPasswordServerWait 30 extern int errno; // GLOBALS RC4_KEY gRC4Key; AuthorizationRef authorization = NULL; char *gKnownMechList[] = { "APOP", "CRAM-MD5", "CRYPT", "DHX", "DIGEST-MD5", "GSSAPI", "KERBEROS_V4", "MS-CHAPv2", "NTLM", "OTP", "SMB-LAN-MANAGER", "SMB-NT", "SMB-NTLMv2", "TWOWAYRANDOM", "WEBDAV-DIGEST", NULL }; // by default, list only optional mechanisms. // filter out mechanisms that are required or unsupported. char *gFilterMechList[] = { "OTP", "DIGEST-MD5", "DHX", "CRYPT", "TWOWAYRANDOM", "CRAM-MD5", "KERBEROS_V4", "NTLM", NULL }; extern "C" { FILE *gLogFileDesc = NULL; bool gNeededToManuallyLaunchPasswordService = false; }; #define debug_and_stderr(A) {debug(A); fprintf(stderr, (A));} //-------------------------------------------------------------------------------------------------- // * main() // //-------------------------------------------------------------------------------------------------- int main ( int argc, char * const *argv ) { sInt32 exitStatus = EX_OK; int commandID; int argLogLimit = 3; bool quiet = false; bool needAuthorization = true; [NSAutoreleasePool new]; #if MYDEBUG gLogFileDesc = fopen( "/Library/Logs/NeST.log", "a+" ); #endif if ( argc < 2 ) { debug( "NeST called with no command.\n" ); _usage( stderr, argv[0] ); exit( 0 ); } char ppidStr[256]; debug_and_stderr( "WARNING: NeST is a deprecated tool. Use slapconfig instead.\n\n" ); char *parentProcName = ProcessName(getppid()); if ( parentProcName == NULL ) parentProcName = strdup( "<unknown>" ); sprintf(ppidStr, "Parent PID: %d\nParent name: %s\n", getppid(), parentProcName); free( parentProcName ); debug_and_stderr( ppidStr ); debug_and_stderr( "Initiating an 8 second delay to call attention to the need to switch tools...\n" ); sleep(4); debug_and_stderr( "4 more seconds to go...\n" ); sleep(4); exitStatus = GetCommandID(argc, argv, needAuthorization, &commandID); switch( commandID ) { // commands not to log case kCmd_getconfig: case kCmd_getstyle: case kCmd_getchildconfig: case kCmd_parentconfig: case kCmd_getpasswordserverstyle: case kCmd_getpasswordserveraddress: case kCmd_getprotocols: break; // commands that can log all args (no passwords) case kCmd_setldapstatic: case kCmd_static: case kCmd_allbindings: case kCmd_setnetinfo: case kCmd_addchild: case kCmd_setprotocols: case kCmd_revokereplica: argLogLimit = argc; // fall through default: debugcat( "\n" ); debug( "command: " ); // only printing the first 3 args to avoid logging passwords for ( int idx = 0; idx < argc && idx < argLogLimit; idx++ ) debugcat( "%s ", argv[idx] ); if (argc > argLogLimit) { debugcat( "...\n" ); } else { debugcat( "\n" ); } } if ( exitStatus == -1 ) { debug( "bad command.\n" ); _usage(stderr, argv[0]); exit(0); } // Anyone can check the version, get help, or get the configuration switch( commandID ) { case kCmd_ver: _version( stderr ); exit( 0 ); break; case kCmd_appleversion: _appleVersion( stderr ); exit( 0 ); break; case kCmd_help: _usage( stderr, argv[0] ); exit( 0 ); break; case kCmd_getconfig: { char configStr[256]; getConfigStr(configStr); printf("%s\n", configStr); exit( 0 ); } break; case kCmd_getstyle: printStyleStr(); exit( 0 ); break; case kCmd_getchildconfig: printChildConfig(); exit( 0 ); break; case kCmd_parentconfig: printParentConfig(); exit( 0 ); break; case kCmd_getpasswordserverstyle: switch(GetPWServerStyle()) { case kPasswordServerNone: printf("0 - none\n"); break; case kPasswordServerUse: printf("1 - use\n"); break; case kPasswordServerHost: printf("2 - host\n"); break; } exit( 0 ); break; case kCmd_getpasswordserveraddress: { char result[1024] = {0}; GetPWServerAddresses(result); printf("%s\n", result); } exit( 0 ); break; case kCmd_getprotocols: { char result[1024] = {0}; GetSASLMechs( result, (argc==2 || strcmp(argv[2], "-a") != 0) ); printf("%s", result); } exit( 0 ); break; } // Only the root user or an admin user can run this app to do anything else if (strcasecmp(argv[argc-1],"-q") == 0) { quiet = true; argc--; } _checkUser( stderr, argv[0], quiet, needAuthorization ); switch( commandID ) { case kCmd_disableldapserver: setLDAPServer(false); break; case kCmd_destroyparent: if ( argc == 3 ) _destroyParent( argv[ 2 ] ); else _destroyParent( "network" ); break; case kCmd_destroyorphanedparent: if ( argc == 3 ) _destroyOrphanedParent( argv[ 2 ] ); else _destroyOrphanedParent( "network" ); break; case kCmd_localonly: _localonly(); break; case kCmd_authserver: setAuthServer(); break; case kCmd_setldapdhcp: _nonetinfo(); setLDAPDHCP(true); break; case kCmd_setldapstatic: _nonetinfo(); setLDAPStatic( argv[ 2 ], argv[ 3 ], argv[ 4 ], argv[ 5 ] ); break; case kCmd_verifypasswordserveradmin: { long result; char idStr[1024]; char *pwPass = NULL; long len; exitStatus = GetCmdLinePass( argv[4], &pwPass, &len ); if ( exitStatus == EX_OK ) { // verify the password result = VerifyAdmin( argv[2], argv[3], pwPass, idStr ); printf( "%s %s\n", (result == eDSNoErr) ? "success" : "failure", idStr ); if ( pwPass != NULL ) { bzero( pwPass, len ); free( pwPass ); } } } break; case kCmd_nopasswordserver: DeletePWServerRecords(); autoLaunchPasswordServer(false, true); //SetServiceState( "pwd", kActionOff, kPasswordServerCmdStr ); if ( argc == 4 ) ConvertLocalUsers( kConvertToBasic, NULL, NULL, argv[2], argv[3] ); #if MYDEBUG if ( argc == 3 && strcmp( argv[2], "clean" ) == 0 ) { FILE *fp; fp = log_popen( "/bin/rm -r /var/db/authserver", "r" ); if ( fp != NULL ) pclose( fp ); ConvertLocalUsers( kConvertToBasic ); } #else if ( argc == 3 ) printf( "user <%s> not converted to basic because a password is required.\n", argv[2] ); #endif break; case kCmd_usepasswordserver: { // format of command: 1 2 3 4 5 6 // NeST -usepasswordserver server-address niuser nipw pwserver-user pwserver-pw long result; char *serverAddress = argv[2]; char *niUserName = argv[3]; char currentAdminIDStr[1024]; char newAdminIDStr[1024]; char *niPass = NULL; char *pwPass = NULL; long niPassLen; long pwPassLen; // before doing anything, we want to erase the passwords from a ps listing // copy and blank arg 4 exitStatus = GetCmdLinePass( argv[4], &niPass, &niPassLen ); if ( exitStatus != EX_OK ) exit( exitStatus ); exitStatus = GetCmdLinePass( argv[6], &pwPass, &pwPassLen ); if ( exitStatus == EX_OK ) { result = VerifyAdmin( serverAddress, argv[5], pwPass, currentAdminIDStr ); if (result == eDSNoErr) { result = NewPWServerAdminUserRemote( serverAddress, currentAdminIDStr, pwPass, niUserName, niPass, newAdminIDStr ); } debugerr( result, "NewPWServerAdminUserRemote=%ld\n", result); if (result == eDSNoErr) { SetPWServerAddress( niUserName, niPass, serverAddress, false, NULL, NULL ); SetAuthAuthority( serverAddress, niUserName, niPass, newAdminIDStr ); } if ( niPass != NULL ) { bzero( niPass, niPassLen ); free( niPass ); } if ( pwPass != NULL ) { bzero( pwPass, pwPassLen ); free( pwPass ); } } } break; case kCmd_hostpasswordserver: exitStatus = HostPasswordServer( argc, argv, quiet ); break; case kCmd_setprotocols: SetSASLMechs(argc, argv); break; case kCmd_rebootnow: { FILE *pf = NULL; pf = log_popen("/sbin/reboot now", "r"); pclose(pf); } break; case kCmd_setupreplica: exitStatus = SetupReplica( argc, argv ); break; case kCmd_convertuser: { char *passwd = NULL; char dbKey[kPWFileMaxPublicKeyBytes] = {0}; char userPass[130] = {0}; char adminPass[130] = {0}; // format of command: 2 3 4 5 // NeST -convertuser niuser dir-admin niuser-password dir-admin-password if ( argc >= 6 ) { // copy and blank the arg ASAP strlcpy( adminPass, argv[5], sizeof(adminPass) ); bzero( argv[5], strlen(adminPass) ); } if ( argc >= 5 ) { // copy and blank the arg ASAP strlcpy( userPass, argv[4], sizeof(userPass) ); bzero( argv[4], strlen(userPass) ); } if ( argc < 5 ) { // getpass() returns a pointer to a static object so it needs to be copied passwd = getpass( "User's new password:"); if ( passwd != NULL ) strlcpy( userPass, passwd, sizeof(userPass) ); } if ( argc < 6 && argc != 3 ) { passwd = getpass( "Administrator password for the directory node:"); if ( passwd != NULL ) strlcpy( adminPass, passwd, sizeof(adminPass) ); } // do not allow blank passwords if ( *userPass == '\0' ) { debug( "blank passwords are not allowed.\n"); fprintf( stderr, "blank passwords are not allowed.\n" ); exit(-1); } if ( ! GetPasswordServerKey( dbKey, sizeof(dbKey) ) ) { debug("cannot get the password server's public key.\n"); fprintf(stderr, "cannot get the password server's public key.\n"); exit(-1); } ConvertUser( dbKey, argv[2], userPass, false, false, (argc>=4) ? argv[3] : NULL, adminPass ); bzero( userPass, sizeof(userPass) ); bzero( adminPass, sizeof(adminPass) ); if ( passwd != NULL ) bzero( passwd, strlen(passwd) ); } break; case kCmd_replicapolicy: break; case kCmd_stripsyncdates: { ReplicaFile *replicaFile = [ReplicaFile new]; [replicaFile stripSyncDates]; [replicaFile saveXMLData]; } break; case kCmd_pwsstandalone: /*exitStatus = */StandAlone( argc, argv ); break; case kCmd_migrateip: // format is: // NeST -migrateip [from|any] to user pass exitStatus = MigrateIP( argc, argv ); break; case kCmd_revokereplica: exitStatus = RevokeReplica( argc, argv ); break; case kCmd_startpasswordserver: { ReplicaFile *replicaFile = [ReplicaFile new]; char ipBuff[256]; if ( ResumePasswordServer() ) { if ( ! PasswordServerListening(kMaxPasswordServerWait) ) { debug( "ERROR: Could not confirm that password server is listening.\n"); fprintf( stderr, "ERROR: Could not confirm that password server is listening.\n" ); exitStatus = EX_UNAVAILABLE; } } exitStatus = SetupPasswordServerConfigRecord( replicaFile, NULL, NULL, ipBuff, false ); [replicaFile free]; } break; case kCmd_stoppasswordserver: autoLaunchPasswordServer(false, true); //SetServiceState( "pwd", kActionOff, kPasswordServerCmdStr ); DeletePWServerRecords(); break; case kCmd_pwsrekey: exitStatus = Rekey( (argc==3) ? argv[2] : NULL ); break; case kCmd_hostpasswordserverinparent: exitStatus = HostPasswordServerInParent( argc, argv, quiet ); break; case kCmd_promote: exitStatus = PromoteToMaster(); break; case kCmd_local_to_shadowhash: exitStatus = LocalToShadowHash(); break; case kCmd_shadowhash_to_ldap: exitStatus = LocalToLDAP(argv[2], quiet); break; default: _usage( stderr, argv[0] ); exit(EX_USAGE); } if ( gNeededToManuallyLaunchPasswordService ) { unsigned long pid = 0; unsigned long watchDogPID = 0; if ( LaunchdRunning( &watchDogPID ) ) { // stop our force-launched process if ( PasswordServerRunning( &pid ) ) { kill( pid, SIGTERM ); sleep(1); if ( PasswordServerRunning( &pid ) ) sleep(1); if ( PasswordServerRunning( &pid ) ) sleep(1); if ( PasswordServerRunning( &pid ) ) kill( pid, SIGKILL ); } } // resume launchd debug( "setting password server back to autolaunch with launchd.\n" ); autoLaunchPasswordServer( true, true ); } if ( exitStatus == EX_USAGE ) _usage( stderr, argv[0] ); exit( exitStatus ); } // main //----------------------------------------------------------------------------- // _usage () // //----------------------------------------------------------------------------- void _usage ( FILE *inFile, const char *inArgv0 ) { static const char * const _szpUsage = "Usage:\t%s\nDo not use this tool. It is deprecated and will be " "removed from 10.5. Use the slapconfig tool instead.\n" " -ver Displays version information.\n" ""; fprintf( inFile, _szpUsage, inArgv0 ); } // _usage //----------------------------------------------------------------------------- // _checkUser () // //----------------------------------------------------------------------------- void _checkUser ( FILE *inFile, const char *inArgv0, bool inQuiet, bool needAuthorization ) { uid_t userID = 99; Boolean userGood = false; userID = getuid(); if ( userID == 0 || !needAuthorization ) { userGood = true; } #if RUNNING_SETUID else { OSStatus status = noErr; AuthorizationItem rights[] = { { "system.preferences", 0, 0, 0 } }; AuthorizationRights rightSet = { sizeof(rights)/ sizeof(*rights), rights }; // prompt for a password struct passwd * user = getpwuid(getuid()); char* prompt = ""; Buffer* password; if ( !inQuiet ) { prompt = "Password:"; } password = getSecretBuffer(prompt); if ( user != NULL && user->pw_name != NULL && password != NULL ) { AuthorizationItem params[] = { {"username", strlen(user->pw_name), (void*)user->pw_name, 0}, {"password", password->length, (void*)password->data, 0} }; AuthorizationEnvironment environment = { sizeof(params)/ sizeof(*params), params }; status = AuthorizationCreate( &rightSet, &environment, kAuthorizationFlagExtendRights, &authorization); } if ( password != NULL ) { bufferRelease( password ); } if ( status == noErr ) { status = AuthorizationCopyRights(authorization, &rightSet, NULL, 0, NULL); if ( status == errAuthorizationSuccess ) { setuid(0); userGood = true; } } } #endif if ( !userGood ) { fprintf( inFile, "Invalid user. \"%s\" must be run by root.\n", inArgv0 ); exit( EX_NOPERM ); } } // _checkUser // -------------------------------------------------------------------------------- // log_popen // -------------------------------------------------------------------------------- FILE *log_popen( const char *inCmd, const char *inMode ) { if ( gLogFileDesc != NULL ) { nest_log_time( gLogFileDesc ); fprintf( gLogFileDesc, " popen: %s, \"%s\"\n", inCmd, inMode ); } return popen( inCmd, inMode ); } // -------------------------------------------------------------------------------- // GetCommandID // -------------------------------------------------------------------------------- int GetCommandID( int argc, char * const *argv, bool & needAuthorization, int *outCommandID ) { int commandID = kCmd_unknown; int argMin = 2; int argMax = 2; const char *p; bool needsRC4Key = false; p = argv[1]; needAuthorization = true; if ( strcmp( p, "-ver" ) == 0 ) // Version { commandID = kCmd_ver; needAuthorization = false; } else if ( strcmp( p, "-appleversion" ) == 0 ) // Internal apple version { commandID = kCmd_appleversion; needAuthorization = false; } else if ( strcmp( p, "-help" ) == 0 ) // Easter egg { commandID = kCmd_help; needAuthorization = false; } else if ( strcmp( p, "-getconfig" ) == 0 ) { commandID = kCmd_getconfig; needAuthorization = false; } else if ( strcmp( p, "-disableldapserver" ) == 0 ) // Disable LDAP on parent { commandID = kCmd_disableldapserver; } else if ( strcmp( p, "-destroyparent" ) == 0 ) // Destroy a parent domain { commandID = kCmd_destroyparent; argMax = 3; } else if ( strcmp( p, "-destroyorphanedparent" ) == 0 ) // Destroy, but don't change bindings { commandID = kCmd_destroyorphanedparent; argMax = 3; } else if ( strcmp( p, "-addchild" ) == 0 ) // Add a child for this server to bind to { commandID = kCmd_addchild; argMin = 4; argMax = 4; } else if ( strcmp( p, "-localonly" ) == 0 ) // set to no bindings { commandID = kCmd_localonly; } else if ( strcmp( p, "-authserver" ) == 0 ) { commandID = kCmd_authserver; } else if ( strcmp( p, "-getstyle" ) == 0 ) { commandID = kCmd_getstyle; needAuthorization = false; } else if ( strcmp( p, "-getchildconfig" ) == 0 ) { commandID = kCmd_getchildconfig; needAuthorization = false; } else if ( strcmp( p, "-getparentconfig" ) == 0 ) { commandID = kCmd_parentconfig; needAuthorization = false; } else if ( strcmp( p, "-setnetinfo" ) == 0 ) { commandID = kCmd_setnetinfo; argMin = 2; argMax = 7; } else if ( strcmp( p, "-setldapdhcp" ) == 0 ) { commandID = kCmd_setldapdhcp; } else if ( strcmp( p, "-setldapstatic" ) == 0 ) { commandID = kCmd_setldapstatic; argMin = 6; argMax = 6; } else if ( strcmp( p, "-getpasswordserverstyle" ) == 0 ) { commandID = kCmd_getpasswordserverstyle; needAuthorization = false; } else if ( strcmp( p, "-getpasswordserveraddress" ) == 0 ) { commandID = kCmd_getpasswordserveraddress; needAuthorization = false; } else if ( strcmp( p, "-verifypasswordserveradmin" ) == 0 ) { commandID = kCmd_verifypasswordserveradmin; argMin = 5; argMax = 5; needsRC4Key = true; needAuthorization = false; } else if ( strcmp( p, "-NOpasswordserver" ) == 0 ) { commandID = kCmd_nopasswordserver; argMax = 4; needsRC4Key = true; } else if ( strcmp( p, "-usepasswordserver" ) == 0 ) { commandID = kCmd_usepasswordserver; argMin = 7; argMax = 7; needsRC4Key = true; } else if ( strcmp( p, "-hostpasswordserver" ) == 0 ) { commandID = kCmd_hostpasswordserver; argMin = 3; argMax = 256; needsRC4Key = true; } else if ( strcmp( p, "-getprotocols" ) == 0 ) { commandID = kCmd_getprotocols; argMax = 3; needAuthorization = false; } else if ( strcmp( p, "-setprotocols" ) == 0 ) { commandID = kCmd_setprotocols; argMin = 4; argMax = 255; } else if ( strcmp( p, "-rebootnow" ) == 0 ) { commandID = kCmd_rebootnow; argMin = 2; argMax = 2; } else if ( strcmp( p, "-setupreplica" ) == 0 ) { // NeST -setupreplica serverIP pw-admin pw-pass commandID = kCmd_setupreplica; argMin = 5; argMax = 256; needsRC4Key = true; } else if ( strcmp( p, "-convertuser" ) == 0 ) { // NeST -convertuser user dir-admin commandID = kCmd_convertuser; argMin = 3; argMax = 6; needsRC4Key = true; } else if ( strcmp( p, "-replicapolicy" ) == 0 ) { commandID = kCmd_replicapolicy; argMin = 3; argMax = 255; } else if ( strcmp( p, "-stripsyncdates" ) == 0 ) { commandID = kCmd_stripsyncdates; } else if ( strcmp( p, "-pwsstandalone" ) == 0 ) { commandID = kCmd_pwsstandalone; argMin = 2; argMax = 255; needsRC4Key = true; } else if ( strcmp( p, "-migrateip" ) == 0 ) { commandID = kCmd_migrateip; argMin = 4; argMax = 6; needsRC4Key = true; } else if ( strcmp( p, "-revokereplica" ) == 0 ) { commandID = kCmd_revokereplica; argMin = 3; argMax = 4; } else if ( strcmp( p, "-startpasswordserver" ) == 0 ) { commandID = kCmd_startpasswordserver; } else if ( strcmp( p, "-stoppasswordserver" ) == 0 ) { commandID = kCmd_stoppasswordserver; } else if ( strcmp( p, "-pwsrekey" ) == 0 ) { commandID = kCmd_pwsrekey; argMax = 3; } else if ( strcmp( p, "-hostpasswordserverinparent" ) == 0 ) { commandID = kCmd_hostpasswordserverinparent; argMin = 3; argMax = 4; } else if ( strcmp( p, "-promote" ) == 0 ) { commandID = kCmd_promote; } else if ( strcmp( p, "--local-to-shadowhash" ) == 0 ) { commandID = kCmd_local_to_shadowhash; } else if ( strcmp( p, "--shadowhash-to-ldap" ) == 0 ) { commandID = kCmd_shadowhash_to_ldap; argMin = 3; argMax = 3; } if ( strcasecmp(argv[argc-1],"-q") == 0 ) { argc--; // ignore -q for purposes of maximum argument enforcement } // do some manipulations to extract our fixed RC4 key if ( needsRC4Key ) { unsigned char stuff[16]; memset(stuff, 0, sizeof(stuff)); strncpy((char *)stuff, _PATH_CONSOLE, 8); stuff[0] = (-22 * 4) & 0xff; stuff[1] = (((int)stuff[0] >> 3) - 3) & 0xff; stuff[2] = ((((int)stuff[0]) & 0240) | 03) & 0xff; stuff[3] = (((int)stuff[1] * 2) - 5) & 0xff; stuff[4] = (((int)stuff[1] << 6) | 023) & 0xff; stuff[5] = (stuff[0] << 4) & 0xff; stuff[6] = (((int)stuff[3] << 6) | 0201) & 0xff; stuff[7] = (stuff[3] + 2) & 0xff; RC4_set_key(&gRC4Key, 8, stuff); memset(stuff, 0, sizeof(stuff)); } *outCommandID = commandID; if ( commandID == kCmd_unknown || argc < argMin || argc > argMax ) return -1; return 0; } //----------------------------------------------------------------------------- // HostPasswordServer // //----------------------------------------------------------------------------- int HostPasswordServer( int argc, char * const *argv, bool quiet ) { long result = eDSNoErr; bool hasServerID = false; char *niPass = NULL; long len = 0; bool ipProvided = false; char curServerAddrStr[256]; char permServerAddrStr[256]; char idStr[1024]; int returnValue = EX_OK; // format of command: 1 2 3 4 (optional) // NeST -hostpasswordserver niuser nipw ip-address if ( argc > 3 ) { result = GetCmdLinePass( argv[3], &niPass, &len ); if ( result != EX_OK ) return result; } GetMyAddressAsString( curServerAddrStr ); ipProvided = ( argc >= 5 ); if ( ipProvided && !ArgsAreIPAddresses( argv, 4, argc-1 ) ) { debug( "An argument after #4 is not an IP address.\n"); fprintf( stderr, "An argument after #4 is not an IP address.\n"); exit( EX_USAGE ); } strlcpy( permServerAddrStr, ipProvided ? argv[4] : curServerAddrStr, sizeof(permServerAddrStr) ); debug( "active ip: %s, configuring ip(s): %s", curServerAddrStr, permServerAddrStr ); if ( argc > 5 ) { for ( int idx = 5; idx < argc; idx++ ) debugcat( ", %s", argv[idx] ); } debugcat( "\n" ); hasServerID = GetAuthAuthority( curServerAddrStr, argv[2], niPass, idStr ); if ( hasServerID ) { bool isAdmin = false; // is the ID still good? result = VerifyUserID( curServerAddrStr, idStr, "", false, &isAdmin ); if ( result == eDSNoErr && isAdmin == false ) { FILE *pf; char cmdStr[256]; char uid[35]; // we are converting a regular user to a password server admin // need to change the user's status strncpy( uid, idStr, 34 ); uid[34] = '\0'; snprintf( cmdStr, sizeof(cmdStr), "/usr/sbin/mkpassdb -setadmin %s", uid ); pf = log_popen( cmdStr, "r" ); result = pclose( pf ); debugerr( result, "error upgrading account to an administrator, exitcode = %ld\n", result ); } if ( result != eDSNoErr ) hasServerID = false; } if ( ! hasServerID ) { // need to add if ( argc == 3 ) { char *ptrToStaticBuf = getpass(quiet ? "":"Password:"); if ( ptrToStaticBuf != NULL ) { len = strlen( ptrToStaticBuf ); if ( len > 511 ) { return EX_USAGE; } niPass = (char *) malloc( len + 1 ); if ( niPass == NULL ) { debug( "memory error\n" ); return -1; } strcpy( niPass, ptrToStaticBuf ); } } // do not allow blank passwords if ( niPass == NULL || *niPass == '\0' ) { debug( "blank passwords are not allowed.\n"); fprintf( stderr, "blank passwords are not allowed.\n"); exit( EX_USAGE ); } PausePasswordServer(); NewPWServerAdminUser( argv[2], niPass ); if ( ResumePasswordServer() ) { // make sure the server has time to start listening if ( ! PasswordServerListening(kMaxPasswordServerWait) ) { debug( "The administrator may not be bound to the password server because the server is not responding." ); fprintf( stderr, "The administrator may not be bound to the password server because the server is not responding.\n" ); } } result = VerifyAdmin( curServerAddrStr, argv[2], niPass, idStr ); debugerr( result, "error creating password server admin, VerifyAdmin = %ld\n", result ); } if ( result == eDSNoErr ) SetAuthAuthority( permServerAddrStr, argv[2], niPass, idStr ); // set the passwordserver config record // must be done after the creation of the database ReplicaFile *replicaFile = [ReplicaFile new]; // make sure the provided IP is in the list if ( ipProvided ) { CFMutableDictionaryRef parentDict = NULL; CFStringRef replicaNameString = NULL; for ( int idx = 4; idx < argc; idx++ ) { replicaNameString = [replicaFile getNameFromIPAddress:argv[idx]]; if ( replicaNameString != NULL && CFStringGetLength(replicaNameString) == 0 ) { // we know we are the parent parentDict = (CFMutableDictionaryRef)[replicaFile getParent]; if ( parentDict != nil ) [replicaFile addIPAddress:argv[idx] toReplica:parentDict]; } } } returnValue = SetupPasswordServerConfigRecord( replicaFile, argv[2], niPass, permServerAddrStr, ipProvided ); SetRealmToHostnameIfPresent(); // clean up if ( niPass != NULL ) { bzero( niPass, len ); free( niPass ); } return returnValue; } //----------------------------------------------------------------------------- // HostPasswordServerInParent // // RETURNS: exitcode // // Command format: NeST -hostpasswordserverinparent admin ip //----------------------------------------------------------------------------- int HostPasswordServerInParent( int argc, char * const *argv, bool quiet ) { long result = eDSNoErr; const char *adminName = argv[2]; bool hasServerID = false; char *niPass = NULL; char curServerAddrStr[256]; char idStr[1024] = {0,}; int returnValue = EX_OK; if ( adminName == NULL || adminName[0] == '\0' ) return EX_USAGE; if ( argc == 3 ) { GetMyAddressAsString( curServerAddrStr ); } else { if ( ! ArgsAreIPAddresses(argv, 3, 3) ) return EX_USAGE; strlcpy( curServerAddrStr, argv[3], sizeof(curServerAddrStr) ); } // get auth authority for user in parent node DSUtilsAuthAuthority dsUtils( adminName, curServerAddrStr, niPass, idStr, true, false ); result = dsUtils.OpenNetInfoParentNode(); if ( result != eDSNoErr ) return EX_UNAVAILABLE; result = dsUtils.DoActionOnCurrentNode(); if ( result != eDSNoErr ) return EX_NOUSER; dsUtils.CopyUserID( idStr ); hasServerID = (idStr[0] != '\0'); if ( hasServerID ) { bool isAdmin = false; // is the ID still good? result = VerifyUserID( curServerAddrStr, idStr, "", false, &isAdmin ); if ( result == eDSNoErr && isAdmin == false ) { FILE *pf; char cmdStr[256]; char uid[35]; // we are converting a regular user to a password server admin // need to change the user's status strncpy( uid, idStr, 34 ); uid[34] = '\0'; snprintf( cmdStr, sizeof(cmdStr), "/usr/sbin/mkpassdb -setadmin %s", uid ); pf = log_popen( cmdStr, "r" ); result = pclose( pf ); debugerr( result, "error upgrading account to an administrator, exitcode = %ld\n", result ); } if ( result != eDSNoErr ) hasServerID = false; } if ( ! hasServerID ) { char *ptrToStaticBuf = getpass(quiet ? "":"Password:"); if ( ptrToStaticBuf != NULL ) { niPass = strdup( ptrToStaticBuf ); if ( niPass == NULL ) { debug( "memory error\n" ); return EX_OSERR; } } // do not allow blank passwords if ( niPass == NULL || *niPass == '\0' ) { debug( "blank passwords are not allowed.\n"); fprintf( stderr, "blank passwords are not allowed.\n"); exit( EX_USAGE ); } PausePasswordServer(); NewPWServerAdminUser( adminName, niPass ); if ( ResumePasswordServer() ) { // make sure the server has time to start listening if ( ! PasswordServerListening(kMaxPasswordServerWait) ) { debug( "The administrator may not be bound to the password server because the server is not responding." ); fprintf( stderr, "The administrator may not be bound to the password server because the server is not responding.\n" ); } } result = VerifyAdmin( curServerAddrStr, adminName, niPass, idStr ); debugerr( result, "error creating password server admin, VerifyAdmin = %ld\n", result ); if ( result == eDSNoErr ) { dsUtils.SetVerifyOnly( false ); dsUtils.SetUserID( idStr ); dsUtils.SetPassword( niPass ); result = dsUtils.DoActionOnCurrentNode(); debugerr( result, "error setting authentication_authority, result = %ld\n", result ); } } // set the passwordserver config record // must be done after the creation of the database ReplicaFile *replicaFile = [ReplicaFile new]; // make sure the provided IP is in the list CFMutableDictionaryRef parentDict = NULL; NSString *replicaNameString = nil; replicaNameString = (NSString *)[replicaFile getNameFromIPAddress:curServerAddrStr]; if ( replicaNameString != nil && [replicaNameString length] == 0 ) { // we know we are the parent parentDict = (CFMutableDictionaryRef) [replicaFile getParent]; if ( parentDict != NULL ) [replicaFile addIPAddress:curServerAddrStr toReplica:parentDict]; } returnValue = SetupPasswordServerConfigRecord( replicaFile, adminName, niPass, curServerAddrStr, true ); [replicaFile free]; SetRealmToHostnameIfPresent(); // clean up if ( niPass != NULL ) { bzero( niPass, strlen(niPass) ); free( niPass ); } return returnValue; } //----------------------------------------------------------------------------- // SetRealmToHostnameIfPresent //----------------------------------------------------------------------------- void SetRealmToHostnameIfPresent( void ) { FILE *pf = NULL; char cmdStr[1024]; char saslRealm[1024] = {0,}; if ( GetHostFromSystemConfiguration(saslRealm, sizeof(saslRealm)) != 0 || saslRealm[0] == '\0' || strstr(saslRealm, ".local") != NULL ) { // The official realm hasn't been set, so generate a unique realm strcpy( saslRealm, "OpenDirectory.XXXXXX" ); mktemp( saslRealm ); } debug( "Setting SASL realm to <%s>\n", saslRealm ); snprintf( cmdStr, sizeof(cmdStr), "/usr/sbin/mkpassdb -setrealm %s", saslRealm ); pf = log_popen( cmdStr, "w" ); if ( pf != NULL ) pclose( pf ); } //----------------------------------------------------------------------------- // GetHostFromSystemConfiguration //----------------------------------------------------------------------------- int GetHostFromSystemConfiguration( char *inOutHostStr, size_t maxHostStrLen ) { int result = -1; SCPreferencesRef scpRef = NULL; try { scpRef = SCPreferencesCreate( NULL, CFSTR("NeST"), 0 ); if ( scpRef == NULL ) throw(1); CFDictionaryRef sysDict = (CFDictionaryRef) SCPreferencesGetValue( scpRef, CFSTR("System") ); if ( sysDict == NULL ) throw(1); CFDictionaryRef sys2Dict = (CFDictionaryRef) CFDictionaryGetValue( sysDict, CFSTR("System") ); if ( sys2Dict == NULL ) throw(1); CFStringRef hostString = (CFStringRef) CFDictionaryGetValue( sys2Dict, CFSTR("HostName") ); if ( hostString == NULL ) throw(1); if ( CFStringGetCString(hostString, inOutHostStr, maxHostStrLen, kCFStringEncodingUTF8) ) result = 0; } catch ( ... ) { result = -1; } if ( scpRef != NULL ) CFRelease( scpRef ); return result; } //----------------------------------------------------------------------------- // SetupPasswordServerConfigRecord // //----------------------------------------------------------------------------- int SetupPasswordServerConfigRecord( ReplicaFile *inReplicaFile, const char *inDirAdmin, const char *inDirPassword, char *inProvidedIPBuff, bool inIPProvided ) { // set the passwordserver config record // must be done after the creation of the database CFStringRef replicaListString = NULL; NSString *replicaIDString = nil; replicaListString = [inReplicaFile xmlString]; debugerr( (replicaListString == NULL), "no replica list.\n" ); replicaIDString = (NSString *)[inReplicaFile getUniqueID]; SetPWServerAddress( inDirAdmin, inDirPassword, inProvidedIPBuff, !inIPProvided, [(NSString *)replicaListString UTF8String], replicaIDString ? [replicaIDString UTF8String] : NULL ); return EX_OK; } //----------------------------------------------------------------------------- // SetupReplica // //----------------------------------------------------------------------------- int SetupReplica( int argc, char * const *argv ) { long len; long syncInterval; char *pwsPass = NULL; NSMutableDictionary *selfDict = nil; id valueRef = nil; AuthDBFile *authFile = nil; bool ipProvided = false; char idStr[1024]; char pubKey[kPWFileMaxPublicKeyBytes]; char privateKey[kPWFileMaxPrivateKeyBytes]; long privateKeyLen = 0; long result; char *replicaList = NULL; struct stat sb; char serverAddrStr[256]; NSString *replicaNameString = nil; char cmdStr[256]; FILE *pf = NULL; // format of command: 2 3 4 // NeST -setupreplica serverIP pwsuser pwspw result = GetCmdLinePass( argv[4], &pwsPass, &len ); if ( result != EX_OK ) return result; ipProvided = ( argc >= 6 ); if ( ipProvided && !ArgsAreIPAddresses( argv, 5, argc-1 ) ) { debug( "An argument after #5 is not an IP address.\n"); fprintf( stderr, "An argument after #5 is not an IP address.\n"); exit(-1); } result = VerifyAdmin( argv[2], argv[3], pwsPass, idStr ); if ( result != eDSNoErr ) { debug( "The administrator password was rejected by the master password server.\n"); fprintf(stderr, "No admin.\n"); exit(-1); } result = GetReplicaSetup( argv[2], idStr, pwsPass, pubKey, privateKey, &privateKeyLen, &replicaList ); if ( result != eDSNoErr || privateKeyLen < 9 ) { debug("GetReplicaSetup = %ld\n", result); if ( privateKeyLen < 9 ) debug("no RSA private key\n"); fprintf(stderr, "GetReplicaSetup = %ld\n", result); exit(-1); } // load replica list ReplicaFile *replicaFile = [[ReplicaFile alloc] initWithXMLStr:replicaList]; UInt32 firstID, lastID; if ( [replicaFile replicaCount] == 0 ) { debug("FAIL: replication list retrieved from the master password server is defective.\n"); debug("%s\n\n", [(NSString *)[replicaFile xmlString] UTF8String]); return EX_CONFIG; } // clear the replica list associated with the current key DeletePWServerRecords( false ); [replicaFile stripSyncDates]; GetMyAddressAsString( serverAddrStr ); replicaNameString = (NSString *)[replicaFile getNameFromIPAddress:serverAddrStr]; // If we're not in the replica table, something is wrong and it's // time to abort if ( replicaNameString == nil || [replicaNameString length] == 0 ) { debug("FAIL: this server's IP address is not in the replication list retrieved from the master password server.\n"); debug("%s\n\n", replicaList); return EX_CONFIG; } [replicaFile getIDRangeForReplica:(CFStringRef)replicaNameString start:&firstID end:&lastID]; selfDict = (NSMutableDictionary *)[replicaFile getReplicaByName:(CFStringRef)replicaNameString]; // make sure the provided IP is in the list if ( ipProvided && selfDict != nil ) { NSString *tempReplicaNameString = nil; for ( int idx = 5; idx < argc; idx++ ) { tempReplicaNameString = (NSString *)[replicaFile getNameFromIPAddress:argv[idx]]; if ( tempReplicaNameString != nil ) [replicaFile addIPAddress:argv[idx] toReplica:(CFMutableDictionaryRef)selfDict]; } } // save a copy of the old replica list if ( lstat( kPWReplicaFile, &sb ) == 0 ) rename( kPWReplicaFile, kPWReplicaFileSavedName ); // Note: the replica list must be saved before changing the database to a replica. Otherwise // the password server may try to synchronize with itself. [replicaFile saveXMLData]; // make our database PausePasswordServer(); if ( MakeReplica(argv[2], pubKey, privateKey, privateKeyLen, firstID, lastID, [replicaNameString UTF8String]) == false ) { // database creation failed debug( "database creation failed.\n"); fprintf(stderr, "database creation failed.\n"); exit(-1); } // construct a slot for the primary administrator PWFileEntry dbEntry = {0}; strlcpy( dbEntry.usernameStr, argv[3], sizeof(dbEntry.usernameStr) ); strlcpy( dbEntry.passwordStr, pwsPass, sizeof(dbEntry.passwordStr) ); dbEntry.access.isAdminUser = 1; dbEntry.access.canModifyPasswordforSelf = 1; authFile = [[AuthDBFile alloc] init]; if ( authFile != NULL ) { result = [authFile validateFiles]; if ( result == 0 ) { idStr[34] = '\0'; if ( pwsf_stringToPasswordRecRef(idStr, &dbEntry) ) result = [authFile setPassword:&dbEntry atSlot:dbEntry.slot obfuscate:YES setModDate:NO]; } [authFile free]; authFile = nil; } // set up replication interval valueRef = [selfDict objectForKey:@"SyncInterval"]; if ( valueRef != nil ) { if ( [valueRef isKindOfClass:[NSNumber class]] ) { syncInterval = [(NSNumber *)valueRef longValue]; sprintf( cmdStr, "/usr/sbin/mkpassdb -setreplicationinterval %ld", syncInterval ); pf = log_popen( cmdStr, "w" ); if ( pf != NULL ) pclose( pf ); } [selfDict removeObjectForKey:@"SyncInterval"]; } // set up SASL realm valueRef = [selfDict objectForKey:@"SASLRealm"]; if ( valueRef != nil ) { if ( [valueRef isKindOfClass:[NSString class]] ) { sprintf( cmdStr, "/usr/sbin/mkpassdb -setrealm %s", [(NSString *)valueRef UTF8String] ); pf = log_popen( cmdStr, "w" ); if ( pf != NULL ) pclose( pf ); } [selfDict removeObjectForKey:@"SASLRealm"]; } [replicaFile saveXMLData]; if ( selfDict != nil ) { [selfDict release]; selfDict = nil; } replicaNameString = (NSString *)[replicaFile getUniqueID]; if ( ipProvided ) strlcpy( serverAddrStr, argv[5], sizeof(serverAddrStr) ); // Note: pass false for <inHost> (4) because we have already retrieved the IP address to use SetPWServerAddress( argv[3], pwsPass, serverAddrStr, false, replicaList, replicaNameString ? [replicaNameString UTF8String] : NULL, kSetupActionSetupReplica ); if ( pwsPass != NULL ) { bzero( pwsPass, len ); free( pwsPass ); } if ( replicaList != NULL ) free( replicaList ); // guarantee running if ( ResumePasswordServer() && !PasswordServerListening(1) ) { debug("WARNING: password server did not start.\n"); } else { ConvertLocalUsers(); // send a HUP signal to start replication PausePasswordServer(); } // remove the local replica cache so the client re-discovers the replica list if ( lstat( kPWReplicaLocalFile, &sb ) == 0 ) unlink( kPWReplicaLocalFile ); // verify that the replica configuration file got updated (paranoia check) { ReplicaFile *verifyRepFile = [ReplicaFile new]; unsigned long pid = 0; if ( [verifyRepFile replicaCount] == 0 ) { // DOH!!! debug("WARNING: replication list verification failed, attempting to resave.\n"); [replicaFile saveXMLData]; // brute-force restart if ( PasswordServerRunning( &pid ) ) { kill( pid, SIGKILL ); if ( gNeededToManuallyLaunchPasswordService ) ResumePasswordServer(); } // verify retry { ReplicaFile *verifyAgainRepFile = [ReplicaFile new]; if ( [verifyAgainRepFile replicaCount] == 0 ) return EX_CONFIG; } } } return EX_OK; } //----------------------------------------------------------------------------- // StandAlone // // Returns: exitcode //----------------------------------------------------------------------------- int StandAlone( int argc, char * const *argv ) { int exitcode = EX_OK; int err; bool passwordServerStopped = false; PWFileEntry userRec; AuthDBFile *authFile = nil; unsigned long modCount = 0; // clear the replica list associated with the current key DeletePWServerRecords( false ); // clear replica list unlink( kPWReplicaFile ); // shutdown our replica passwordServerStopped = StopPasswordServer(); // save off the database temporarily to extract our local users if ( DatabaseExists() ) { unlink( kPWFileSavedName ); rename( kPWFilePath, kPWFileSavedName ); } if ( argc == 2 ) { PausePasswordServer(); NewPWServerAdminUser( "disabled-slot-0x1", "temporary" ); try { authFile = [[AuthDBFile alloc] init]; if ( authFile == NULL ) throw( (int)EX_SOFTWARE ); err = [authFile validateFiles]; if ( err != 0 ) throw( (int)EX_NOINPUT ); userRec.slot = 1; err = [authFile getPasswordRec:1 putItHere:&userRec unObfuscate:NO]; if ( err != 0 ) throw( (int)EX_NOINPUT ); userRec.access.isDisabled = true; err = [authFile setPassword:&userRec atSlot:1 obfuscate:NO setModDate:YES]; if ( err != 0 ) throw( (int)EX_SOFTWARE ); } catch( int catchErr ) { unlink( kPWFilePath ); exitcode = catchErr; } [authFile free]; if ( exitcode == EX_OK ) { if ( ResumePasswordServer() ) { // make sure the server has time to start listening if ( ! PasswordServerListening(kMaxPasswordServerWait) ) { debug( "The administrator may not be bound to the password server because the server is not responding." ); fprintf( stderr, "The administrator may not be bound to the password server because the server is not responding.\n" ); } } } } else { // create a new database (-hostpasswordserver command) exitcode = HostPasswordServer( argc, argv ); } if ( exitcode == EX_OK ) { // transfer the password server records for local users modCount = ConvertLocalUsers( kConvertToNewEmptyDatabase ); // revoke the database from the replicated system (the cheese stands alone) unlink( kPWFileSavedName ); } else { rename( kPWFileSavedName, kPWFilePath ); } if ( modCount == 0 ) { // no accounts using password server, shut it off passwordServerStopped = StopPasswordServer(); DeletePWServerRecords(); } else { // if it didn't stop, ResumePasswordServer() will not start it so do it here if ( !passwordServerStopped ) autoLaunchPasswordServer( true, true ); } return exitcode; } //----------------------------------------------------------------------------- // Rekey // // Returns: exitcode //----------------------------------------------------------------------------- int Rekey( const char *inBitCountStr ) { int exitcode = EX_OK; FILE *pf = NULL; unsigned int bitCount = 0; int idx = 0; char curChar; char commandBuf[256]; char dbKey[kPWFileMaxPublicKeyBytes] = {0,}; // get the bit count arg if ( inBitCountStr != NULL ) sscanf( inBitCountStr, "%u", &bitCount ); if ( bitCount != 0 && bitCount != 1024 && bitCount != 2048 && bitCount != 3072 ) return EX_USAGE; // shutdown our password server if ( ! StopPasswordServer() ) return EX_SOFTWARE; // clear the replica list associated with the current key DeletePWServerRecords( false ); // rekey the database if ( bitCount == 0 ) sprintf( commandBuf, "/usr/sbin/mkpassdb -rekeydb" ); else sprintf( commandBuf, "/usr/sbin/mkpassdb -rekeydb %u", bitCount ); pf = log_popen( commandBuf, "r" ); if ( pf != NULL ) { do { fscanf( pf, "%c", &curChar ); if ( curChar != '\n' ) dbKey[idx++] = curChar; } while ( curChar != '\n' && idx < (int)sizeof(dbKey) ); dbKey[idx] = '\0'; exitcode = pclose( pf ); } // update the local NetInfo config record if present { ReplicaFile *replicaFile = [ReplicaFile new]; NSString *xmlDataString = (NSString *)[replicaFile xmlString]; long status; if ( xmlDataString != nil ) { status = SetPWConfigAttributeLocal( kPWConfigDefaultRecordName, kDS1AttrPasswordServerList, [xmlDataString UTF8String], false ); if ( status != eDSRecordNotFound ) debugerr( status, "WARNING: did not update the local NetInfo /config/passwordserver record (error = %ld).\n", status ); [xmlDataString release]; } } // rekey the users in the directory ConvertLocalUsers( kConvertToNewRSAKey, dbKey ); // restart password server ResumePasswordServer(); // output the new key printf( "%s\n", dbKey ); return exitcode; } //----------------------------------------------------------------------------- // MigrateIP // // Returns: exitcode // // This command is used when promoting an LDAP replica to become the master. // It performs the migration of user records and the PasswordServerLocation // attribute for Jaguar. For other IP migration scenarios, use the changeip // tool instead. //----------------------------------------------------------------------------- int MigrateIP( int argc, char * const *argv ) { char *ptrToStaticBuf = NULL; long len; char *admin = NULL; char *pwsPass = NULL; long result; bool bRestrictToGivenIP; const char *oldIP = NULL; char newIP[256]; // format of command: 2 3 4 5 // NeST -migrateip from to dir-user dir-pw // decrypt password if ( argc == 6 ) { result = GetCmdLinePass( argv[5], &pwsPass, &len ); if ( result != EX_OK ) return result; admin = argv[4]; } else if ( argc == 5 ) { // prompt for password char promptStr[256]; admin = argv[4]; snprintf( promptStr, sizeof(promptStr), "Password for %s:", admin ); ptrToStaticBuf = getpass( promptStr ); if ( ptrToStaticBuf == NULL ) return EX_SOFTWARE; len = strlen( ptrToStaticBuf ); pwsPass = (char *) malloc( len + 1 ); if ( pwsPass == NULL ) return EX_SOFTWARE; strcpy( pwsPass, ptrToStaticBuf ); bzero( ptrToStaticBuf, len ); } strlcpy( newIP, argv[3], sizeof(newIP) ); bRestrictToGivenIP = ( strcasecmp( argv[2], "all" ) != 0 ); oldIP = bRestrictToGivenIP ? argv[2] : NULL; ConvertLocalUsers( kConvertToNewIPAddress, oldIP, newIP ); ConvertLDAPUsers( admin, pwsPass, kConvertToNewIPAddress, oldIP, newIP ); SetPWServerAddress( admin, pwsPass, newIP, false, NULL, NULL ); RemovePWSListAttributeAndPWSReplicaRecord( admin, pwsPass ); if ( pwsPass != NULL ) { bzero( pwsPass, strlen(pwsPass) ); free( pwsPass ); } return EX_OK; } //----------------------------------------------------------------------------- // RevokeReplica // // Returns: exitcode <sysexits.h> // Format: NeST -revokereplica [replica-name|replica-IP] [domain-admin] //----------------------------------------------------------------------------- int RevokeReplica( int argc, char * const *argv ) { char *ptrToStaticBuf = NULL; NSString *xmlDataString = nil; NSMutableDictionary *replicaDict = nil; ReplicaFile *replicaFile = [ReplicaFile new]; NSString *replicaNameString = nil; NSString *replicaIDString = nil; if ( ArgsAreIPAddresses( argv, 2, 2 ) ) { replicaNameString = (NSString *)[replicaFile getNameFromIPAddress:argv[2]]; if ( replicaNameString == nil ) return EX_CONFIG; } else { replicaNameString = [NSString stringWithUTF8String:argv[2]]; } replicaDict = (NSMutableDictionary *)[replicaFile getReplicaByName:(CFStringRef)replicaNameString]; if ( replicaDict == NULL ) return EX_CONFIG; [replicaFile setReplicaStatus:kReplicaPermissionDenied forReplica:(CFMutableDictionaryRef)replicaDict]; [replicaFile saveXMLData]; xmlDataString = (NSString *)[replicaFile xmlString]; if ( xmlDataString == nil ) return EX_SOFTWARE; replicaIDString = (NSString *)[replicaFile getUniqueID]; // if a domain admin name is provided, prompt for the password if ( argc == 4 ) { char promptStr[256]; snprintf( promptStr, sizeof(promptStr), "Password for %s:", argv[3] ); ptrToStaticBuf = getpass( promptStr ); if ( ptrToStaticBuf == NULL ) return EX_SOFTWARE; SetPWServerAddress( argv[3], ptrToStaticBuf, NULL, false, [xmlDataString UTF8String], replicaIDString ? [replicaIDString UTF8String] : NULL, kSetupActionRevokeReplica ); } else { SetPWServerAddress( NULL, NULL, NULL, false, [xmlDataString UTF8String], replicaIDString ? [replicaIDString UTF8String] : NULL, kSetupActionRevokeReplica ); } // clean up [xmlDataString release]; [replicaDict release]; if ( ptrToStaticBuf != NULL ) bzero( ptrToStaticBuf, strlen(ptrToStaticBuf) ); return EX_OK; } //----------------------------------------------------------------------------- // ArgsAreIPAddresses () //----------------------------------------------------------------------------- bool ArgsAreIPAddresses ( char * const *argv, int firstArg, int lastArg ) { int index; char ptonResult[sizeof(struct in_addr) + 500]; bool result = true; for ( index = firstArg; index <= lastArg; index++ ) { if ( inet_pton(AF_INET, argv[index], &ptonResult) != 1 ) { result = false; break; } } return result; } //----------------------------------------------------------------------------- // PromoteToMaster () //----------------------------------------------------------------------------- int PromoteToMaster( void ) { AuthDBFile *authFile = [[AuthDBFile alloc] init]; ReplicaFile *replicaFile = [ReplicaFile new]; NSMutableDictionary *curRepDict; NSMutableDictionary *parentDict; NSString *replicaNameString = nil; int err; PWFileHeader dbHeader; // get our replica name err = [authFile validateFiles]; if ( err == 0 ) err = [authFile getHeader:&dbHeader]; if ( err != 0 ) { debug("Error: Could not validate the password server database.\n"); fprintf(stderr, "Error: Could not validate the password server database.\n"); return EX_OSFILE; } replicaNameString = [NSString stringWithUTF8String:dbHeader.replicationName]; // retrieve the two items in the table to change curRepDict = (NSMutableDictionary *)[replicaFile getReplicaByName:(CFStringRef)replicaNameString]; parentDict = (NSMutableDictionary *) [replicaFile getParent]; if ( curRepDict == NULL || parentDict == NULL ) { debug("Error: Could not validate the replica table.\n"); fprintf(stderr, "Error: Could not validate the replica table.\n"); return EX_OSFILE; } // retrieve data to transfer NSObject *ipValue = [[curRepDict objectForKey:@kPWReplicaIPKey] retain]; NSString *dnsValue = [[curRepDict objectForKey:@kPWReplicaDNSKey] retain]; NSString *idBeginValue = [[curRepDict objectForKey:@kPWReplicaIDRangeBeginKey] retain]; NSString *idEndValue = [[curRepDict objectForKey:@kPWReplicaIDRangeEndKey] retain]; // Note: not checking the DNS key (optional key) if ( ipValue == NULL || idBeginValue == NULL || idEndValue == NULL ) { debug("Error: Could not extract data from the replica table.\n"); fprintf(stderr, "Error: Could not extract data from the replica table.\n"); return EX_CONFIG; } // we're ready, shut down the server for the transformation if ( ! StopPasswordServer() ) { debug("Error: Could not shut down the password server.\n"); fprintf(stderr, "Error: Could not shut down the password server.\n"); return EX_TEMPFAIL; } // update the table [replicaFile decommissionReplica:(CFStringRef)replicaNameString]; [replicaFile stripSyncDates]; // Note: The DNS key must be cleared because there may not be a replacement [parentDict removeObjectForKey:@kPWReplicaDNSKey]; // some other keys to remove [parentDict removeObjectForKey:@kPWReplicaPolicyKey]; [parentDict removeObjectForKey:@kPWReplicaSyncDateKey]; [parentDict removeObjectForKey:@kPWReplicaSyncServerKey]; [parentDict removeObjectForKey:@kPWReplicaStatusKey]; [parentDict removeObjectForKey:@kPWReplicaSyncAttemptKey]; [parentDict removeObjectForKey:@kPWReplicaIncompletePullKey]; // replace values [parentDict setObject:ipValue forKey:@kPWReplicaIPKey]; [parentDict setObject:idBeginValue forKey:@kPWReplicaIDRangeBeginKey]; [parentDict setObject:idEndValue forKey:@kPWReplicaIDRangeEndKey]; if ( dnsValue != NULL ) { [parentDict setObject:dnsValue forKey:@kPWReplicaDNSKey]; [dnsValue release]; } [ipValue release]; [idBeginValue release]; [idEndValue release]; // update the parent entry's mod date [replicaFile setEntryModDateForReplica:(CFMutableDictionaryRef)parentDict]; // save [replicaFile saveXMLData]; // update identity bzero( dbHeader.replicationName, sizeof(dbHeader.replicationName) ); err = [authFile setHeader:&dbHeader]; if ( err != 0 ) { fprintf(stderr, "Error: Could not update the server's identity.\n"); return EX_IOERR; } // restart the server as the parent if ( ! ResumePasswordServer() ) { fprintf(stderr, "Warning: Could not start the password server.\n"); } return EX_OK; } //----------------------------------------------------------------------------- // LocalToShadowHash //----------------------------------------------------------------------------- int LocalToShadowHash(void) { unsigned long recordsModified; recordsModified = ConvertLocalUsers(kConvertToShadowHash); printf("%lu record%s modified.\n", recordsModified, (recordsModified==1) ? "" : "s"); return EX_OK; } //----------------------------------------------------------------------------- // LocalToLDAP //----------------------------------------------------------------------------- int LocalToLDAP(const char *inAdminUser, bool quiet) { unsigned long recordsModified = 0; char *ldapPass = NULL; int len; char *ptrToStaticBuf = getpass(quiet ? "":"Password:"); if ( ptrToStaticBuf != NULL ) { len = strlen( ptrToStaticBuf ); if ( len > 511 ) { return EX_USAGE; } ldapPass = (char *) malloc( len + 1 ); if ( ldapPass == NULL ) { debug( "memory error\n" ); return EX_OSERR; } strlcpy( ldapPass, ptrToStaticBuf, len + 1 ); } recordsModified = MigrateLocalToLDAP(inAdminUser, ldapPass); printf("%lu record%s modified.\n", recordsModified, (recordsModified==1) ? "" : "s"); return EX_OK; } //----------------------------------------------------------------------------- // MigrateLocalToLDAP // // Returns: the number of records that were modified // //----------------------------------------------------------------------------- unsigned long MigrateLocalToLDAP( const char *inAdminUser, const char *inAdminPass ) { tDirReference dsRef = 0; tDataBuffer *tDataBuff = NULL; tDirNodeReference nodeRef = 0; long status = eDSNoErr; tContextData context = nil; unsigned long index = 0; unsigned long nodeCount = 0; tDataList *recordNameList = nil; tDataList *recordTypeList = nil; tDataList *attributeList = nil; unsigned long recIndex = 0; unsigned long recCount = 0; tRecordEntry *recEntry = nil; tAttributeListRef attrListRef = 0; unsigned long modCount = 0; /* char *userName = nil; char *tptr = nil; char *version; char *tag; char *data; */ DSUtils dsUtils; //PWFileEntry userRec; status = dsUtils.GetLocallyHostedNodeList(); if ( status != eDSNoErr ) return modCount; dsRef = dsUtils.GetDSRef(); do { tDataBuff = dsDataBufferAllocate( dsRef, 4096 ); if ( tDataBuff == NULL ) break; recordNameList = dsBuildListFromStrings( dsRef, kDSRecordsAll, nil ); recordTypeList = dsBuildListFromStrings( dsRef, kDSStdRecordTypeUsers, nil ); attributeList = dsBuildListFromStrings( dsRef, kDSAttributesAll, nil ); nodeCount = dsUtils.GetLocallyHostedNodeCount(); for ( index = 1; index <= nodeCount; index++ ) { status = dsUtils.OpenLocallyHostedNode( index ); if ( status != eDSNoErr ) continue; nodeRef = dsUtils.GetCurrentNodeRef(); do { status = dsGetRecordList( nodeRef, tDataBuff, recordNameList, eDSExact, recordTypeList, attributeList, false, &recCount, &context ); if (status != eDSNoErr) break; for ( recIndex = 1; recIndex <= recCount; recIndex++ ) { status = dsGetRecordEntry( nodeRef, tDataBuff, recIndex, &attrListRef, &recEntry ); if ( status != eDSNoErr && recEntry == NULL ) continue; } } while ( status == eDSNoErr && context != NULL ); } } while(false); if ( tDataBuff != NULL ) { dsDataBufferDeAllocate( dsRef, tDataBuff ); tDataBuff = NULL; } return modCount; } //----------------------------------------------------------------------------- // _version () // //----------------------------------------------------------------------------- void _version ( FILE *inFile ) { static const char * const _ver = "NetInfo Setup Tool (NeST), Apple Computer, Inc., Version 2.2\n"; fprintf( inFile, _ver ); } // _version //----------------------------------------------------------------------------- // _appleVersion () // //----------------------------------------------------------------------------- void _appleVersion ( FILE *inFile ) { static const char * const _ver = "NetInfo Setup Tool (NeST), Apple Computer, Inc., Version 255\n"; fprintf( inFile, _ver ); } // _appleVersion //----------------------------------------------------------------------------- // _localonly () // //----------------------------------------------------------------------------- long _localonly( void ) { setDSSearchPolicy( 2 ); // local only return _nonetinfo(); } // _localonly //----------------------------------------------------------------------------- // _nonetinfo () // //----------------------------------------------------------------------------- long _nonetinfo( void ) { bool scpStatus = true; SCPreferencesRef scpRef; long status = 0; scpRef = SCPreferencesCreate( NULL, CFSTR("NeST"), 0 ); if ( scpRef == NULL ) { return -1; } _removeBindings( scpRef ); _setInactiveFlag( scpRef ); scpStatus = SCPreferencesCommitChanges( scpRef ); scpStatus = SCPreferencesApplyChanges( scpRef ); CFRelease( scpRef ); return( status ); } // _nonetinfo //----------------------------------------------------------------------------- // _destroyParent () // //----------------------------------------------------------------------------- void _destroyParent ( const char *inTag ) { bool scpStatus = true; SCPreferencesRef scpRef; char string[ 256 ]; // kill LDAP server setLDAPServer( false ); setDSSearchPolicy( 2 ); // local only scpRef = SCPreferencesCreate( NULL, CFSTR("NeST"), 0 ); if ( scpRef != NULL ) { _removeBindings( scpRef ); _setInactiveFlag( scpRef ); scpStatus = SCPreferencesCommitChanges( scpRef ); scpStatus = SCPreferencesApplyChanges( scpRef ); CFRelease( scpRef ); sleep( 2 ); } snprintf( string, sizeof(string), "/usr/sbin/nidomain -d %s", inTag ); system( string ); snprintf( string, sizeof(string), "/var/db/netinfo/.%s.tim", inTag ); remove(string); setNIServer(false); } // _destroyParent //----------------------------------------------------------------------------- // _destroyOrphanedParent () // //----------------------------------------------------------------------------- void _destroyOrphanedParent ( const char *inTag ) { char string[ 256 ]; // kill LDAP server setLDAPServer( false ); snprintf( string, sizeof(string), "/usr/sbin/nidomain -d %s", inTag ); system( string ); snprintf( string, sizeof(string), "/var/db/netinfo/.%s.tim", inTag ); remove(string); setNIServer(false); } // _destroyParent //----------------------------------------------------------------------------- // _removeBindings () // //----------------------------------------------------------------------------- void _removeBindings ( SCPreferencesRef scpRef ) { bool scpStatus = true; char *pCurrSet = nil; CFStringRef cfNetInfoKeyPath; CFTypeRef cfTypeRef; char cArray1[ 2048 ]; // Modify the system config file sprintf( cArray1, kBindingPath, "/Sets/0" ); // Get the current set cfTypeRef = SCPreferencesGetValue( scpRef, CFSTR( "CurrentSet" ) ); if ( cfTypeRef != NULL ) { pCurrSet = (char *)CFStringGetCStringPtr( (CFStringRef)cfTypeRef, kCFStringEncodingMacRoman ); if ( pCurrSet != nil ) { sprintf( cArray1, kNetInfoPath, pCurrSet ); } } cfNetInfoKeyPath = CFStringCreateWithCString( kCFAllocatorDefault, cArray1, kCFStringEncodingMacRoman ); // Remove any old binding methods scpStatus = SCPreferencesPathRemoveValue( scpRef, cfNetInfoKeyPath ); CFRelease( cfNetInfoKeyPath ); } // _removeBindings //----------------------------------------------------------------------------- // _setInactiveFlag () // //----------------------------------------------------------------------------- void _setInactiveFlag ( SCPreferencesRef scpRef ) { bool scpStatus = true; unsigned long uiDataLen = 0; char *pCurrSet = nil; CFStringRef cfInactive; CFTypeRef cfTypeRef; char cArray1[ 2048 ]; CFDataRef dataRef; CFDictionaryRef dictRef; CFPropertyListRef plistRef; _removeBindings( scpRef ); // Set the new binding method to broadcast uiDataLen = strlen( kInactive ); dataRef = CFDataCreate( nil, (const UInt8 *)kInactive, uiDataLen ); if ( dataRef != nil ) { plistRef = CFPropertyListCreateFromXMLData( kCFAllocatorDefault, dataRef, kCFPropertyListImmutable, nil ); if ( plistRef != nil ) { dictRef = (CFDictionaryRef)plistRef; sprintf( cArray1, kNetInfoPath, "/Sets/0" ); cfInactive = CFStringCreateWithCString( kCFAllocatorDefault, cArray1, kCFStringEncodingMacRoman ); // Get the current set cfTypeRef = SCPreferencesGetValue( scpRef, CFSTR( "CurrentSet" ) ); if ( cfTypeRef != NULL ) { pCurrSet = (char *)CFStringGetCStringPtr( (CFStringRef)cfTypeRef, kCFStringEncodingMacRoman ); if ( pCurrSet != nil ) { sprintf( cArray1, kNetInfoPath, pCurrSet ); cfInactive = CFStringCreateWithCString( kCFAllocatorDefault, cArray1, kCFStringEncodingMacRoman ); } } scpStatus = SCPreferencesPathSetValue( scpRef, cfInactive, dictRef ); CFRelease( cfInactive ); CFRelease( dictRef ); } } } // _setInactiveFlag //------------------------------------------------------------------------------------------------ // GetPWServerStyle //------------------------------------------------------------------------------------------------ PWServerStyle GetPWServerStyle( void ) { char addressStr[256]; char configAddrStr[256] = {0}; char *tptr; // get this machine's IP GetMyAddressAsString( addressStr ); // get the IP from the config record GetPWServerAddresses(configAddrStr); tptr = strchr(configAddrStr, ','); if ( tptr ) *tptr = '\0'; if ( *configAddrStr == '\0' ) return kPasswordServerNone; else if ( strcmp( addressStr, configAddrStr ) == 0 ) return kPasswordServerHost; return kPasswordServerUse; } void DeletePWServerRecords( bool removeDefaultIPRec ) { DSUtilsDeleteRecords dsUtils( removeDefaultIPRec ); /*long status = (long)*/ dsUtils.DoActionForAllLocalNodes(); } bool DatabaseExists( void ) { struct stat sb; int err = -1; err = lstat( kPWFilePath, &sb ); return ( err == 0 ); } //----------------------------------------------------------------------------- // MakeReplica // // Returns: false if function exits and no database exists //----------------------------------------------------------------------------- bool MakeReplica( const char *inUserName, const char *inPubicKey, const char *inPrivateKey, long inPrivateKeyLen, unsigned long inFirstSlot, unsigned long inNumSlots, const char *inReplicaName ) { // mkpassdb -a -u %s -p -q FILE *pf; bool result = true; bool hasDatabase; char *encodedPrivateKey; char cmdStr[256]; if ( inUserName == nil || inPubicKey == nil || inPrivateKey == nil ) return false; hasDatabase = DatabaseExists(); if ( hasDatabase ) rename( kPWFilePath, kPWFileSavedName ); encodedPrivateKey = (char *)malloc( inPrivateKeyLen * 2 + 1 ); ConvertBinaryToHex( (const unsigned char *)inPrivateKey, inPrivateKeyLen, encodedPrivateKey ); sprintf( cmdStr, "/usr/sbin/mkpassdb -zoq -s %lu -e %lu", inFirstSlot, inNumSlots ); if ( inReplicaName != NULL && *inReplicaName != '\0' ) { strcat( cmdStr, " -n " ); strcat( cmdStr, inReplicaName ); } pf = log_popen(cmdStr, "w"); if (pf != nil) { fprintf(pf, "%s\n", inPubicKey); fprintf(pf, "%s\n", encodedPrivateKey); pclose(pf); } free( encodedPrivateKey ); if ( ! hasDatabase ) result = DatabaseExists(); return result; } //----------------------------------------------------------------------------- // GetPasswordServerKey // // Returns: TRUE if the key was obtained. //----------------------------------------------------------------------------- bool GetPasswordServerKey( char *key, long maxlen ) { FILE *fp; char curChar; int idx = 0; bool loaded = false; fp = log_popen( "/usr/sbin/mkpassdb -key", "r" ); if ( fp != NULL ) { do { fscanf( fp, "%c", &curChar ); if ( curChar != '\n' ) key[idx++] = curChar; } while ( curChar != '\n' && idx < maxlen ); key[idx] = '\0'; loaded = true; pclose(fp); } return loaded; } //----------------------------------------------------------------------------- // ConvertUser // // Returns: void //----------------------------------------------------------------------------- void ConvertUser( const char *currentPasswordServerKey, const char *userName, const char *password, bool isHashData, bool isAdminUser, const char *dirAdmin, const char *dirAdminPass ) { char *tptr; char addressStr[256]; char idStr[2048]; bool hasServerID = false; char *userNodeName = NULL; DSUtils dsUtils; // get the current password server address //GetPWServerAddresses( addressStr ); tDirStatus status = dsUtils.GetServerAddressForUser( userName, addressStr, &userNodeName ); if ( status != eDSNoErr || addressStr[0] == '\0' ) { printf("no password server\n"); exit( EX_UNAVAILABLE ); } tptr = strchr(addressStr, ','); if ( tptr != NULL ) *tptr = '\0'; // do not allow blank passwords if ( *password == '\0' ) { debug( "blank passwords are not allowed.\n"); exit( EX_USAGE ); } hasServerID = GetAuthAuthority( addressStr, userName, password, idStr ); if ( hasServerID ) { // does the key match our local database? char *userKey; userKey = strchr( idStr, ',' ); if ( userKey != NULL && currentPasswordServerKey != NULL ) { // if the keys match, the user is already a valid password server user if ( strcmp( userKey + 1, currentPasswordServerKey ) == 0 ) { debug( "user key matches password server key.\n"); return; } } } // need to add PausePasswordServer(); if ( NewPWServerAddUser( userName, password, isAdminUser ) == false ) { // database creation failed debug( "could not add user, maybe no database.\n" ); fprintf(stderr, "could not add user, maybe no database.\n"); exit(-1); } // guarantee running ResumePasswordServer(); status = (tDirStatus) VerifyUser( "127.0.0.1", userName, password, true, idStr ); if ( status == eDSNoErr ) { DSUtilsAuthAuthority dsUtils( userName, addressStr, password, idStr, false, false ); status = dsUtils.OpenNodeByName( userNodeName, dirAdmin, dirAdminPass ); if ( status == eDSNoErr ) { char realmName[256]; status = dsUtils.DoActionOnCurrentNode(); if ( status == eDSNoErr ) { if ( AddPrincipal( userName, password, realmName, sizeof(realmName) ) == 0 ) { dsUtils.SetAuthTypeToKerberos( realmName ); status = dsUtils.DoActionOnCurrentNode(); } } else { debug( "Error: Could not modify the user's record (error = %d).\n", status ); } } else { debug( "Error: Could not open directory node: %s (error = %d).\n", userNodeName, status ); } } else { debug( "Error: Could not verify the user has a password server account (error = %d).", status ); } if ( userNodeName != NULL ) free( userNodeName ); } void AddUserToNewDatabase( const char *currentPasswordServerKey, const char *userName, PWFileEntry *pwRec, const char *dirAdmin, const char *password, bool keepSlotNumber ) { char *tptr; bool hasServerID = false; char addressStr[256]; char idStr[1024]; int err; AuthDBFile *authFile = [[AuthDBFile alloc] init]; if ( authFile == nil ) return; err = [authFile validateFiles]; if ( err != 0 ) return; // get the current password server address GetPWServerAddresses( addressStr ); if ( addressStr[0] == '\0' ) { GetMyAddressAsString( addressStr ); if ( addressStr[0] == '\0' ) { printf("no password server\n"); exit(0); } } tptr = strchr(addressStr, ','); if ( tptr != NULL ) *tptr = '\0'; hasServerID = GetAuthAuthority( addressStr, userName, "", idStr ); if ( hasServerID ) { // does the key match our local database? char *userKey; userKey = strchr( idStr, ',' ); if ( userKey != NULL && currentPasswordServerKey != NULL ) { // if the keys match, the user is already a valid password server user if ( strcmp( userKey + 1, currentPasswordServerKey ) == 0 ) { debug( "user key matches password server key.\n"); return; } } } // need to add PausePasswordServer(); if ( keepSlotNumber ) err = [authFile addPassword:pwRec atSlot:pwRec->slot obfuscate:NO]; else err = [authFile addPassword:pwRec obfuscate:NO]; if ( authFile != nil ) { [authFile closePasswordFile]; [authFile free]; } // guarantee running ResumePasswordServer(); if ( err == 0 ) { pwsf_passwordRecRefToString( pwRec, idStr ); idStr[34] = ','; strlcpy( idStr + 35, currentPasswordServerKey, sizeof(idStr) - 35 ); SetAuthAuthority( addressStr, userName, "", idStr, dirAdmin, password ); } } //----------------------------------------------------------------------------- // ConvertLocalUsers // // Returns: the number of records that were modified // // Gets all password server users from the directory and re-adds them to // the password server after it becomes a replica of a greater system. // It can also convert to basic or a new IP address. //----------------------------------------------------------------------------- unsigned long ConvertLocalUsers( ConvertTarget inConvertToWhat, const char *inParam1, const char *inNewIP, const char *inAdminUser, const char *inAdminPass ) { tDirReference dsRef = 0; tDataBuffer *tDataBuff = NULL; tDirNodeReference nodeRef = 0; long status = eDSNoErr; tContextData context = nil; tAttributeValueEntry *pExistingAttrValue = NULL; unsigned long index = 0; unsigned long nodeCount = 0; bool hasServerID = false; unsigned long attrValueIDToReplace = 0; tDataList *recordNameList = nil; tDataList *recordTypeList = nil; tDataList *attributeList = nil; unsigned long recIndex = 0; unsigned long recCount = 0; char *authAuthorityStr = nil; tRecordEntry *recEntry = nil; tAttributeListRef attrListRef = 0; FILE *fp = nil; char *userName = nil; char dbKey[kPWFileMaxPublicKeyBytes] = {0}; char *tptr = nil; unsigned long modCount = 0; const char *inOldIP = inParam1; const char *inNewRSAKey = inParam1; char *version; char *tag; char *data; DSUtils dsUtils; PWFileEntry userRec; status = dsUtils.GetLocallyHostedNodeList(); if ( status != eDSNoErr ) return modCount; dsRef = dsUtils.GetDSRef(); do { tDataBuff = dsDataBufferAllocate( dsRef, 4096 ); if ( tDataBuff == NULL ) break; if ( inConvertToWhat == kConvertToNewDatabase || inConvertToWhat == kConvertToNewEmptyDatabase ) { if ( ! GetPasswordServerKey( dbKey, sizeof(dbKey) ) ) return modCount; fp = fopen( kPWFileSavedName, "r" ); if ( fp == NULL ) return modCount; } recordNameList = dsBuildListFromStrings( dsRef, (inAdminUser != NULL) ? inAdminUser : kDSRecordsAll, nil ); recordTypeList = dsBuildListFromStrings( dsRef, kDSStdRecordTypeUsers, nil ); attributeList = dsBuildListFromStrings( dsRef, kDSNAttrAuthenticationAuthority, kDSNAttrGroupMembership, kDSNAttrRecordName, nil ); nodeCount = dsUtils.GetLocallyHostedNodeCount(); for ( index = 1; index <= nodeCount; index++ ) { // initialize state hasServerID = false; pExistingAttrValue = nil; attrValueIDToReplace = 0; status = dsUtils.OpenLocallyHostedNode( index ); if ( status != eDSNoErr ) continue; nodeRef = dsUtils.GetCurrentNodeRef(); do { status = dsGetRecordList( nodeRef, tDataBuff, recordNameList, eDSExact, recordTypeList, attributeList, false, &recCount, &context ); if (status != eDSNoErr) break; for ( recIndex = 1; recIndex <= recCount; recIndex++ ) { status = dsGetRecordEntry( nodeRef, tDataBuff, recIndex, &attrListRef, &recEntry ); if ( status != eDSNoErr && recEntry == NULL ) continue; status = GetDataFromDataBuff( dsRef, nodeRef, tDataBuff, recEntry, attrListRef, &authAuthorityStr, &userName ); if ( status == eDSNoErr && authAuthorityStr != NULL ) { switch( inConvertToWhat ) { case kConvertToBasic: SetAuthAuthorityToBasic( userName, inAdminPass ? inAdminPass : "admin" ); modCount++; break; case kConvertToNewDatabase: if ( GetPasswordDataForID( fp, authAuthorityStr, &userRec ) ) { AddUserToNewDatabase( dbKey, userName, &userRec, NULL, NULL, NO ); modCount++; } break; case kConvertToNewEmptyDatabase: if ( GetPasswordDataForID( fp, authAuthorityStr, &userRec ) ) { AddUserToNewDatabase( dbKey, userName, &userRec, NULL, NULL, (userRec.slot==1) ); modCount++; } break; case kConvertToNewIPAddress: status = dsParseAuthAuthority( authAuthorityStr, &version, &tag, &data ); if ( status == eDSNoErr ) { // tag is already checked in GetDataFromDataBuff() tptr = strchr( data, ':' ); if ( tptr != NULL ) { if ( inOldIP == NULL || strcmp( inOldIP, tptr + 1 ) == 0 ) { *tptr = '\0'; SetAuthAuthority( inNewIP, userName, "", data ); modCount++; } } if ( version != NULL ) free( version ); if ( tag != NULL ) free( tag ); if ( data != NULL ) free( data ); } break; case kConvertToNewRSAKey: status = dsParseAuthAuthority( authAuthorityStr, &version, &tag, &data ); if ( status == eDSNoErr ) { // tag is already checked in GetDataFromDataBuff() tptr = strchr( data, ':' ); if ( tptr != NULL ) { char newAAData[kPWFileMaxPublicKeyBytes+36]; strlcpy( newAAData, data, 36 ); strlcat( newAAData, inNewRSAKey, sizeof(newAAData) ); SetAuthAuthority( tptr + 1, userName, "", newAAData ); modCount++; } if ( version != NULL ) free( version ); if ( tag != NULL ) free( tag ); if ( data != NULL ) free( data ); } break; case kConvertToShadowHash: /* if ( GetPasswordDataForID( fp, authAuthorityStr, &userRec ) ) { unsigned char hashes[kHashTotalLength] = {0}; UInt32 hashesLen; // make shadowhash if ( userRec.access.passwordIsHash ) { memcpy( outHashes, &userRec.digest[kPWHashSlotSMB_NT].digest[1], 32 ); memcpy( outHashes + kHashShadowOneLength, &userRec.digest[kPWHashSlotSMB_LAN_MANAGER].digest[1], 32 ); memcpy( outHashes + kHashOffsetToCramMD5, &userRec.digest[kPWHashSlotCRAM_MD5].digest[1], 64 ); } else { pwsf_DESAutoDecode( "1POTATO2", userRec.passwordStr ); GenerateShadowHashes( userRec.passwordStr, strlen(userRec.passwordStr), kNiPluginHashDefaultServerSet, NULL, hashes, &hashesLen ); } modCount++; } */ break; } } if ( authAuthorityStr != NULL ) { free( authAuthorityStr ); authAuthorityStr = NULL; } if ( userName != NULL ) { free( userName ); userName = NULL; } } } while ( status == eDSNoErr && context != NULL ); } } while(false); if ( fp != NULL ) fclose( fp ); if ( tDataBuff != NULL ) { dsDataBufferDeAllocate( dsRef, tDataBuff ); tDataBuff = NULL; } return modCount; } //-------------------------------------------------------------------------------------------------- // ConvertLDAPUsers // // Returns: ds err or -1 for non-DS errors //-------------------------------------------------------------------------------------------------- long ConvertLDAPUsers( const char *inLDAPAdmin, const char *inAdminPass, ConvertTarget inConvertToWhat, const char *inOldIP, const char *inNewIP ) { tDirReference dsRef = 0; tDataBuffer *tDataBuff = NULL; tDirNodeReference nodeRef = 0; long status = eDSNoErr; tContextData context = nil; tAttributeValueEntry *pExistingAttrValue = NULL; unsigned long attrValueIDToReplace = 0; tDataList *recordNameList = nil; tDataList *recordTypeList = nil; tDataList *attributeList = nil; unsigned long recIndex = 0; unsigned long recCount = 0; char *authAuthorityStr = nil; tRecordEntry *recEntry = nil; tAttributeListRef attrListRef = 0; FILE *fp = nil; char *userName = nil; char dbKey[kPWFileMaxPublicKeyBytes] = {0}; char *tptr = nil; unsigned long buffSize = 4096; char *version; char *tag; char *data; DSUtils dsUtils; PWFileEntry userRec; if ( inConvertToWhat == kConvertToBasic || inConvertToWhat == kConvertToNewRSAKey ) return -1; status = dsUtils.OpenLocalLDAPNode( inLDAPAdmin, inAdminPass ); if ( status != eDSNoErr ) return status; dsRef = dsUtils.GetDSRef(); tDataBuff = dsDataBufferAllocate( dsRef, buffSize ); if ( tDataBuff == NULL ) return -1; if ( inConvertToWhat == kConvertToNewDatabase || inConvertToWhat == kConvertToNewEmptyDatabase ) { if ( ! GetPasswordServerKey( dbKey, sizeof(dbKey) ) ) return -1; fp = fopen( kPWFileSavedName, "r" ); if ( fp == NULL ) return -1; } try { recordNameList = dsBuildListFromStrings( dsRef, kDSRecordsAll, nil ); recordTypeList = dsBuildListFromStrings( dsRef, kDSStdRecordTypeUsers, nil ); attributeList = dsBuildListFromStrings( dsRef, kDSNAttrAuthenticationAuthority, kDSNAttrRecordName, nil ); // initialize state pExistingAttrValue = nil; attrValueIDToReplace = 0; nodeRef = dsUtils.GetCurrentNodeRef(); do { do { status = dsGetRecordList( nodeRef, tDataBuff, recordNameList, eDSExact, recordTypeList, attributeList, false, &recCount, &context ); if ( status == eDSBufferTooSmall ) { buffSize *= 2; // a safety for a runaway condition if ( buffSize > 1024 * 1024 ) throw( (long)eDSBufferTooSmall ); dsDataBufferDeAllocate( dsRef, tDataBuff ); tDataBuff = dsDataBufferAllocate( dsRef, buffSize ); if ( tDataBuff == NULL ) throw( (long)eMemoryError ); } } while ( status == eDSBufferTooSmall ); if ( status != eDSNoErr ) throw( status ); for ( recIndex = 1; recIndex <= recCount; recIndex++ ) { status = dsGetRecordEntry( nodeRef, tDataBuff, recIndex, &attrListRef, &recEntry ); if ( status != eDSNoErr && recEntry == NULL ) continue; status = GetDataFromDataBuff( dsRef, nodeRef, tDataBuff, recEntry, attrListRef, &authAuthorityStr, &userName ); if ( status == eDSNoErr && authAuthorityStr != NULL ) { switch( inConvertToWhat ) { case kConvertToBasic: case kConvertToNewRSAKey: // not supported break; case kConvertToNewDatabase: case kConvertToNewEmptyDatabase: if ( GetPasswordDataForID( fp, authAuthorityStr, &userRec ) ) AddUserToNewDatabase( dbKey, userName, &userRec, inLDAPAdmin, inAdminPass ); break; case kConvertToNewIPAddress: status = dsParseAuthAuthority( authAuthorityStr, &version, &tag, &data ); if ( status == eDSNoErr ) { // tag is already checked in GetDataFromDataBuff() tptr = strchr( data, ':' ); if ( tptr != NULL ) { if ( inOldIP == NULL || strcmp( inOldIP, tptr + 1 ) == 0 ) { *tptr = '\0'; //SetAuthAuthority( inNewIP, userName, "", data, inLDAPAdmin, inAdminPass ); { long status = eDSNoErr; try { if (inNewIP == nil) throw( -1 ); DSUtilsAuthAuthority dsUtils( userName, inNewIP, "", data, false, false ); dsUtils.CopyUserID( data ); if ( inLDAPAdmin != NULL && inAdminPass != NULL ) { status = dsUtils.OpenLocalLDAPNode( inLDAPAdmin, inAdminPass ); if (status != eDSNoErr) throw( status ); status = dsUtils.DoActionOnCurrentNode(); dsUtils.CopyUserID( data ); if (status != eDSNoErr) throw( status ); } } catch(long catchErr) { status = catchErr; debugerr( catchErr, "HandleAuthAuthority error = %ld\n", catchErr ); } catch(...) { debug( "HandleAuthAuthority: an unexpected error occurred.\n" ); } } } } if ( version != NULL ) free( version ); if ( tag != NULL ) free( tag ); if ( data != NULL ) free( data ); } break; case kConvertToShadowHash: // not valid for LDAP break; } } if ( authAuthorityStr != NULL ) { free( authAuthorityStr ); authAuthorityStr = NULL; } if ( userName != NULL ) { free( userName ); userName = NULL; } } } while ( status == eDSNoErr && context != NULL ); } catch( long catchErr ) { status = catchErr; } catch(...) { status = -1; } if ( fp != NULL ) fclose( fp ); if ( tDataBuff != NULL ) { dsDataBufferDeAllocate( dsRef, tDataBuff ); tDataBuff = NULL; } return status; } //-------------------------------------------------------------------------------------------------- // GetDataFromDataBuff //-------------------------------------------------------------------------------------------------- long GetDataFromDataBuff( tDirReference dsRef, tDirNodeReference inNodeRef, tDataBuffer *tDataBuff, tRecordEntry *pRecEntry, tAttributeListRef attrListRef, char **outAuthAuthority, char **outRecordName ) { sInt32 error = eDSNoErr; uInt32 j = 0; uInt32 k = 0; char *pRecNameStr = nil; char *pRecTypeStr = nil; tAttributeValueListRef valueRef = 0; tAttributeEntry *pAttrEntry = nil; tAttributeValueEntry *pValueEntry = nil; // Do not initialize to NULL; this method may be called multiple times // and we do not want to stomp on a successful result //*outAuthAuthority = NULL; *outRecordName = NULL; if ( inNodeRef != 0 && pRecEntry != nil ) { error = dsGetRecordNameFromEntry( pRecEntry, &pRecNameStr ); if ( error == eDSNoErr ) error = dsGetRecordTypeFromEntry( pRecEntry, &pRecTypeStr ); if ( error == eDSNoErr ) { // DEBUG if ( true ) { debug( "\n" ); debug( " Record Name = %s\n", pRecNameStr ); debug( " Record Type = %s\n", pRecTypeStr ); debug( " Attribute count = %ld\n", pRecEntry->fRecordAttributeCount ); } for ( j = 1; (j <= pRecEntry->fRecordAttributeCount) && (error == eDSNoErr); j++ ) { error = dsGetAttributeEntry( inNodeRef, tDataBuff, attrListRef, j, &valueRef, &pAttrEntry ); if ( error == eDSNoErr && pAttrEntry != NULL ) { for ( k = 1; (k <= pAttrEntry->fAttributeValueCount) && (error == eDSNoErr); k++ ) { error = dsGetAttributeValue( inNodeRef, tDataBuff, k, valueRef, &pValueEntry ); if ( error == eDSNoErr && pValueEntry != NULL ) { if ( strstr( pValueEntry->fAttributeValueData.fBufferData, "ApplePasswordServer" ) != NULL ) { *outAuthAuthority = (char *) malloc( pValueEntry->fAttributeValueData.fBufferLength + 1 ); strcpy( *outAuthAuthority, pValueEntry->fAttributeValueData.fBufferData ); break; } // DEBUG if ( true ) { debug( " %ld - %ld: (%s) %s\n", j, k, pAttrEntry->fAttributeSignature.fBufferData, pValueEntry->fAttributeValueData.fBufferData ); } dsDeallocAttributeValueEntry( dsRef, pValueEntry ); pValueEntry = NULL; } else { //PrintError( kErrGetAttributeEntry, error ); } } dsDeallocAttributeEntry( dsRef, pAttrEntry ); pAttrEntry = NULL; dsCloseAttributeValueList(valueRef); valueRef = 0; } else { //PrintError( kErrGetAttributeEntry, error ); } } delete( pRecTypeStr ); pRecTypeStr = nil; } else { //PrintError( kErrGetRecordTypeFromEntry, error ); } *outRecordName = pRecNameStr; dsDeallocRecordEntry( dsRef, pRecEntry ); pRecEntry = NULL; dsCloseAttributeList( attrListRef ); attrListRef = 0; } return( error ); } // GetDataFromDataBuff //----------------------------------------------------------------------------- // GetPasswordDataForID // // Returns: TRUE if successful //----------------------------------------------------------------------------- bool GetPasswordDataForID( FILE *fp, const char *authAuthorityStr, PWFileEntry *outUserRecord ) { char *version; char *tag; char *data; long status; char userID[35]; bool success = false; if ( fp == NULL || authAuthorityStr == NULL || outUserRecord == NULL ) return false; status = dsParseAuthAuthority( authAuthorityStr, &version, &tag, &data ); if ( status == eDSNoErr ) { strncpy( userID, data, 34 ); userID[34] = '\0'; success = GetObfuscatedPasswordForID( fp, userID, outUserRecord ); } if ( version != NULL ) free( version ); if ( tag != NULL ) free( tag ); if ( data != NULL ) free( data ); return success; } //----------------------------------------------------------------------------- // GetObfuscatedPasswordForID // // Returns: TRUE if successful //----------------------------------------------------------------------------- bool GetObfuscatedPasswordForID( FILE *fp, const char *inUserID, PWFileEntry *outUserRecord ) { off_t pos; PWFileEntry slotRec; PWFileEntry pwRec; ssize_t byteCount; bool success = false; if ( fp == NULL || inUserID == NULL || outUserRecord == NULL ) return false; if ( pwsf_stringToPasswordRecRef( inUserID, &slotRec ) ) { pos = pwsf_slotToOffset( slotRec.slot ); byteCount = pread( fileno(fp), &pwRec, sizeof(pwRec), pos ); if ( byteCount == sizeof(pwRec) && slotRec.time == pwRec.time && slotRec.rnd == pwRec.rnd && slotRec.sequenceNumber == pwRec.sequenceNumber && slotRec.slot == pwRec.slot ) { memcpy( outUserRecord, &pwRec, sizeof(pwRec) ); success = true; } } return success; } //----------------------------------------------------------------------------- // NewPWServerAdminUser // // Returns: false if function exits and no database exists //----------------------------------------------------------------------------- bool NewPWServerAdminUser( const char *inUserName, const char *inPassword ) { return NewPWServerAddUser( inUserName, inPassword, true ); } //----------------------------------------------------------------------------- // NewPWServerAddUser // // Returns: false if function exits and no database exists //----------------------------------------------------------------------------- bool NewPWServerAddUser( const char *inUserName, const char *inPassword, bool admin ) { // mkpassdb -[a|b] -u %s -p -q FILE *pf; char commandStr[512]; bool result = true; bool hasDatabase; if ( inUserName == nil || inPassword == nil ) return false; hasDatabase = DatabaseExists(); if ( hasDatabase ) { // database exists, add a new admin snprintf(commandStr, sizeof(commandStr), "/usr/sbin/mkpassdb -%c -u %s -p -q", admin ? 'a' : 'b', inUserName); } else { if ( ! admin ) return false; snprintf(commandStr, sizeof(commandStr), "/usr/sbin/mkpassdb -u %s -p -q", inUserName); } pf = log_popen(commandStr, "w"); if (pf != nil) { fprintf(pf, "%s\n", inPassword); pclose(pf); } if ( ! hasDatabase ) result = DatabaseExists(); return result; } long NewPWServerAdminUserRemote( const char *inServerAddress, const char *inAdminID, const char *inAdminPassword, const char *inNewAdminName, const char *inNewAdminPassword, char *outID ) { tDirReference dsRef = 0; tDataBuffer *tDataBuff = 0; unsigned long len; tDataNode *pAuthType = nil; tDataBuffer *pStepBuff = nil; long status = eDSNoErr; tDirNodeReference nodeRef = 0; char userID[1024] = {0}; DSUtils dsUtils; do { if ( inServerAddress == nil || inAdminID == nil || inAdminPassword == nil || inNewAdminName == nil || inNewAdminPassword == nil || outID == nil ) { status = -1; break; } status = dsUtils.OpenSpecificPasswordServerNode( inServerAddress ); if (status != eDSNoErr) break; dsRef = dsUtils.GetDSRef(); nodeRef = dsUtils.GetCurrentNodeRef(); tDataBuff = dsDataBufferAllocate( dsRef, 2048 ); if (tDataBuff == 0) break; pStepBuff = dsDataBufferAllocate( dsRef, 2048 ); if (pStepBuff == 0) break; pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthNewUser ); if ( pAuthType == nil ) break; status = dsUtils.FillAuthBuff( tDataBuff, 4, strlen(inAdminID), inAdminID, strlen(inAdminPassword), inAdminPassword, strlen(inNewAdminName), inNewAdminName, strlen(inNewAdminPassword), inNewAdminPassword ); if ( status != eDSNoErr ) break; // dsDoDirNodeAuth -- get new user status = dsDoDirNodeAuth( nodeRef, pAuthType, false, tDataBuff, pStepBuff, nil ); if ( status != eDSNoErr ) break; // extract the ID memcpy(&len, pStepBuff->fBufferData, 4); pStepBuff->fBufferData[len+4] = '\0'; if ( len > 4 && len < 1024 ) { strncpy( userID, pStepBuff->fBufferData + 4, len ); userID[len] = '\0'; } // clean up if ( pAuthType ) { dsDataBufferDeAllocate( dsRef, pAuthType ); pAuthType = nil; } tDataBuff->fBufferLength = 0; pStepBuff->fBufferLength = 0; status = dsUtils.FillAuthBuff( tDataBuff, 4, strlen(inAdminID), inAdminID, strlen(inAdminPassword), inAdminPassword, strlen(userID), userID, strlen(kSetAdminUserStr), kSetAdminUserStr ); if ( status != eDSNoErr ) break; // set policy to admin user pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthSetPolicy ); if ( pAuthType == nil ) break; // TEMP WORKAROUND /* if (nodeRef != 0) { dsCloseDirNode(nodeRef); nodeRef = 0; } pDataList = dsBuildFromPath( dsRef, pwServerNodeStr, "/" ); status = dsOpenDirNode( dsRef, pDataList, &nodeRef ); dsDataListDeallocate( dsRef, pDataList ); free( pDataList ); pDataList = nil; if (status != eDSNoErr) break; */ status = dsDoDirNodeAuth( nodeRef, pAuthType, false, tDataBuff, pStepBuff, nil ); if ( status != eDSNoErr ) break; } while(false); strcpy( outID, userID ); if ( tDataBuff ) dsDataBufferDeAllocate( dsRef, tDataBuff ); if ( pStepBuff ) dsDataBufferDeAllocate( dsRef, pStepBuff ); if ( pAuthType ) dsDataBufferDeAllocate( dsRef, pAuthType ); return status; } //----------------------------------------------------------------------------- // GetAuthAuthority // // Returns TRUE if the user is a password server user and returns the user ID // in <inOutUserID> //----------------------------------------------------------------------------- Boolean GetAuthAuthority( const char *inServerAddress, const char *inUsername, const char *inPassword, char *inOutUserID ) { if ( inOutUserID == nil ) { debug("GetAuthAuthority(): inOutUserID must be non-null\n"); exit(-1); } *inOutUserID = '\0'; HandleAuthAuthority( inServerAddress, inUsername, inPassword, inOutUserID, true ); return( *inOutUserID != '\0' ); } //----------------------------------------------------------------------------- // SetAuthAuthority //----------------------------------------------------------------------------- void SetAuthAuthority( const char *inServerAddress, const char *inUsername, const char *inPassword, char *inOutUserID, const char *dirAdmin, const char *dirAdminPass ) { HandleAuthAuthority( inServerAddress, inUsername, inPassword, inOutUserID, false, false, dirAdmin, dirAdminPass ); } //----------------------------------------------------------------------------- // SetAuthAuthorityToBasic //----------------------------------------------------------------------------- void SetAuthAuthorityToBasic( const char *inUsername, const char *inPassword ) { char userID[1024]; HandleAuthAuthority( "0.0.0.0", inUsername, inPassword, userID, false, true ); } //----------------------------------------------------------------------------- // HandleAuthAuthority //----------------------------------------------------------------------------- void HandleAuthAuthority( const char *inServerAddress, const char *inUsername, const char *inPassword, char *inOutUserID, Boolean inVerifyOnly, Boolean inSetToBasic, const char *dirAdmin, const char *dirAdminPass ) { long status = eDSNoErr; try { if (inServerAddress == nil) throw( -1 ); DSUtilsAuthAuthority dsUtils( inUsername, inServerAddress, inPassword, inOutUserID, inVerifyOnly, inSetToBasic ); dsUtils.DoActionForAllLocalNodes(); dsUtils.CopyUserID( inOutUserID ); if ( dirAdmin != NULL && dirAdminPass != NULL ) { status = dsUtils.OpenLocalLDAPNode( dirAdmin, dirAdminPass ); if (status != eDSNoErr) throw( status ); status = dsUtils.DoActionOnCurrentNode(); dsUtils.CopyUserID( inOutUserID ); if (status != eDSNoErr) throw( status ); } } catch(long catchErr) { status = catchErr; debugerr( catchErr, "HandleAuthAuthority error = %ld\n", catchErr ); } catch(...) { debug( "HandleAuthAuthority: an unexpected error occurred.\n" ); } } void GetSASLMechs(char *outSASLStr, Boolean filterMechs) { FILE *pf; char curChar; char saslStr[1024]; char saslMechStr[256]; char *tptr = saslStr; char *endPtr = nil; long index, index2; long len; long saslPrefixLen; bool needsPlain; Boolean skipMech; Boolean knownMechListAvail[] = {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false}; if ( outSASLStr == nil ) return; *outSASLStr = '\0'; pf = popen("/usr/sbin/mkpassdb -list", "r"); if ( pf != nil ) { // get the raw string do { fscanf(pf, "%c", &curChar); *tptr++ = curChar; } while ( curChar != ')' && curChar != '\n' ); *tptr = '\0'; pclose(pf); // sanity checks saslPrefixLen = strlen(kSASLPrefixStr); len = strlen(saslStr); if ( len > saslPrefixLen && strncmp(saslStr, kSASLPrefixStr, saslPrefixLen) == 0 && saslStr[len-1] == ')' ) { // strip off front and back saslStr[len-2] = '\0'; tptr = saslStr + saslPrefixLen; // add info do { endPtr = strchr(tptr+1, '"'); if (!endPtr) return; // assume we need a plain text password unless proven otherwise needsPlain = true; // set the enabled flag strlcpy(saslMechStr, tptr + 1, endPtr - tptr); // filter out mechanisms our UI doesn't want skipMech = false; if ( filterMechs ) { for ( index = 0; gFilterMechList[index] != NULL; index++ ) { if ( strcmp( saslMechStr, gFilterMechList[index] ) == 0 ) { skipMech = true; break; } } } if ( !skipMech ) { // find out if the mechanism is enabled and if it can store a hash on disk for ( index = 0; gKnownMechList[index] != NULL; index++ ) { if ( !knownMechListAvail[index] && strcmp( saslMechStr, gKnownMechList[index] ) == 0 ) { knownMechListAvail[index] = true; break; } } // add to the return string strncat(outSASLStr, tptr, endPtr - tptr + 1); strcat(outSASLStr, " Enabled "); if ( pwsf_GetSASLMechInfo( saslMechStr, NULL, &needsPlain ) ) strcat(outSASLStr, needsPlain ? "Plain\n" : "Hash\n"); else strcat(outSASLStr, "Unknown\n"); } tptr = endPtr + 1; if ( *tptr ) tptr++; } while (*tptr); // add the disabled mechs for ( index = 0; gKnownMechList[index] != NULL; index++ ) { // filter out mechanisms our UI doesn't want skipMech = false; if ( filterMechs ) { for ( index2 = 0; gFilterMechList[index2] != NULL; index2++ ) { if ( strcmp( gKnownMechList[index], gFilterMechList[index2] ) == 0 ) { skipMech = true; break; } } } if ( !skipMech && !knownMechListAvail[index] ) { strcat(outSASLStr, "\""); strcat(outSASLStr, gKnownMechList[index]); strcat(outSASLStr, "\" Disabled "); if ( pwsf_GetSASLMechInfo( gKnownMechList[index], NULL, &needsPlain ) ) strcat(outSASLStr, needsPlain ? "Plain\n" : "Hash\n"); else strcat(outSASLStr, "Unknown\n"); } } } } if ( *outSASLStr == '\0' ) strcat(outSASLStr, "\n"); } void SetSASLMechs( int argc, char * const *argv ) { int argIndex; bool changeMade = false; bool enable; for ( argIndex = 2; argIndex < argc - 1; argIndex++ ) { enable = (strcasecmp( argv[argIndex + 1], "on" ) == 0); if ( enable || strcasecmp( argv[argIndex + 1], "off" ) == 0 ) if ( pwsf_SetSASLPluginState( argv[argIndex], enable ) ) changeMade = true; } // send HUP signal if ( changeMade ) PausePasswordServer(); } //----------------------------------------------------------------------------- // VerifyAdmin // // Returns: DS Error Code //----------------------------------------------------------------------------- long VerifyAdmin( const char *inServerAddress, const char *inUserName, const char *inPassword, char *outID ) { return VerifyUser( inServerAddress, inUserName, inPassword, false, outID ); } //----------------------------------------------------------------------------- // VerifyUser // // Returns: DS Error Code //----------------------------------------------------------------------------- long VerifyUser( const char *inServerAddress, const char *inUserName, const char *inPassword, bool inCheckAllUsers, char *outID ) { tDirReference dsRef = 0; tDataBuffer *tDataBuff = 0; unsigned long len; tDataNode *pAuthType = nil; tDataBuffer *pStep1Buff = nil; tDataBuffer *pStepBuff = nil; long status = eDSAuthFailed; tDirNodeReference nodeRef = 0; char userID[1280] = {0}; char rsaKey[1024] = {0}; char *startPtr, *endPtr; const long bufferSize = 2048; DSUtils dsUtils; do { if ( inServerAddress == nil || inUserName == nil || inPassword == nil || outID == nil ) { status = eParameterError; break; } *outID = '\0'; status = dsUtils.OpenSpecificPasswordServerNode( inServerAddress ); debugerr( status, "VerifyUser, OpenSpecificPasswordServerNode = %ld\n", status ); if (status != eDSNoErr) break; dsRef = dsUtils.GetDSRef(); nodeRef = dsUtils.GetCurrentNodeRef(); tDataBuff = dsDataBufferAllocate( dsRef, bufferSize ); if (tDataBuff == 0) { status = eMemoryError; break; } pStep1Buff = dsDataBufferAllocate( dsRef, bufferSize ); if (pStep1Buff == 0) { status = eMemoryError; break; } pStepBuff = dsDataBufferAllocate( dsRef, bufferSize ); if (pStepBuff == 0) { status = eMemoryError; break; } // first get the admin's user ID pAuthType = dsDataNodeAllocateString( dsRef, "dsAuthMethodNative:dsAuthGetIDByName" ); if ( pAuthType == nil ) { status = eMemoryError; break; } status = dsUtils.FillAuthBuff( tDataBuff, 3, strlen(inUserName), inUserName, strlen(inPassword), inPassword, strlen(inUserName), inUserName ); if (status != eDSNoErr) break; if ( inCheckAllUsers ) { // ALL len = strlen("ALL"); memcpy( tDataBuff->fBufferData + tDataBuff->fBufferLength, &len, 4 ); tDataBuff->fBufferLength += 4; memcpy( tDataBuff->fBufferData + tDataBuff->fBufferLength, "ALL", len ); tDataBuff->fBufferLength += len; } // dsDoDirNodeAuth status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStep1Buff, nil ); debugerr( status, "VerifyUser, dsDoDirNodeAuth - dsAuthGetIDByName = %ld\n", status ); if ( status != eDSNoErr ) break; // extract the ID and try to verify the password until we // find the right one (annoying) memcpy(&len, pStep1Buff->fBufferData, 4); if ( len < 4 ) { status = eDSAuthResponseBufTooSmall; break; } if ( len > bufferSize - 5 ) { status = eDSInvalidBuffFormat; break; } pStep1Buff->fBufferData[len+4] = '\0'; // get the RSA Key startPtr = strchr( pStep1Buff->fBufferData + 4, ',' ); if ( startPtr != NULL ) strlcpy( rsaKey, startPtr, sizeof(rsaKey) ); startPtr = pStep1Buff->fBufferData + 4; do { // get an ID endPtr = strchr( startPtr, ';' ); if ( endPtr != NULL ) { strncpy( userID, startPtr, endPtr - startPtr ); userID[endPtr - startPtr] = '\0'; startPtr = endPtr + 1; // append the RSA key strlcat( userID, rsaKey, sizeof(userID) ); } else { strlcpy( userID, startPtr, sizeof(userID) ); startPtr = NULL; // don't append the RSA key (already attached) } debug( "userID = %s\n", userID ); // clean up if ( pAuthType ) { dsDataBufferDeAllocate( dsRef, pAuthType ); pAuthType = nil; } tDataBuff->fBufferLength = 0; pStepBuff->fBufferLength = 0; if ( !inCheckAllUsers ) { // getpolicy does not require an authentication, but the buffer format // takes one anyway in case the behavior changes. // admin name status = dsUtils.FillAuthBuff( tDataBuff, 3, strlen(userID), userID, strlen(inPassword), inPassword, strlen(userID), userID ); if (status != eDSNoErr) break; // get policy pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthGetPolicy ); if ( pAuthType == nil ) { status = eMemoryError; break; } status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStepBuff, nil ); debugerr( status, "VerifyUser, dsDoDirNodeAuth - kDSStdAuthGetPolicy = %ld\n", status ); if ( status != eDSNoErr ) break; memcpy(&len, pStepBuff->fBufferData, 4); pStepBuff->fBufferData[len+4] = '\0'; if ( len > 4 ) { char *adminStr = strstr(pStepBuff->fBufferData+4, kIsAdminPolicyStr); if ( adminStr == nil ) continue; adminStr += strlen(kIsAdminPolicyStr); if ( *adminStr == '0' ) continue; } // clean up if ( pAuthType ) { dsDataBufferDeAllocate( dsRef, pAuthType ); pAuthType = nil; } pStepBuff->fBufferLength = 0; } // verify password tDataBuff->fBufferLength = 0; status = dsUtils.FillAuthBuff( tDataBuff, 2, strlen(userID), userID, strlen(inPassword), inPassword ); if (status != eDSNoErr) break; pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthNodeNativeNoClearText ); if ( pAuthType == nil ) { status = eMemoryError; break; } status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStepBuff, nil ); debugerr( status, "VerifyUser, dsDoDirNodeAuth - kDSStdAuthNodeNativeNoClearText = %ld\n", status ); if ( status == eDSNoErr ) { strcpy( outID, userID ); break; } } while ( startPtr != NULL && endPtr != NULL ); } while( false ); if ( *outID == '\0' ) status = eDSAuthUnknownUser; // clean up if ( tDataBuff ) dsDataBufferDeAllocate( dsRef, tDataBuff ); if ( pStep1Buff ) dsDataBufferDeAllocate( dsRef, pStep1Buff ); if ( pStepBuff ) dsDataBufferDeAllocate( dsRef, pStepBuff ); if ( pAuthType ) dsDataBufferDeAllocate( dsRef, pAuthType ); debugerr( status, "VerifyUser = %ld\n", status ); return status; } //----------------------------------------------------------------------------- // VerifyUserID // // Returns: DS Error Code // // succeeds if the UserID is valid. //----------------------------------------------------------------------------- long VerifyUserID( const char *inServerAddress, const char *inUserID, const char *inPassword, bool inVerifyPassword, bool *outIsAdmin ) { tDirReference dsRef = 0; tDataBuffer *tDataBuff = 0; unsigned long len; tDataNode *pAuthType = nil; tDataBuffer *pStep1Buff = nil; tDataBuffer *pStepBuff = nil; long status = eDSAuthFailed; tDirNodeReference nodeRef = 0; const long bufferSize = 2048; DSUtils dsUtils; do { if ( inServerAddress == nil || inUserID == nil || inPassword == nil ) { status = eParameterError; break; } if ( outIsAdmin != NULL ) *outIsAdmin = false; status = dsUtils.OpenSpecificPasswordServerNode( inServerAddress ); if (status != eDSNoErr) break; dsRef = dsUtils.GetDSRef(); nodeRef = dsUtils.GetCurrentNodeRef(); tDataBuff = dsDataBufferAllocate( dsRef, bufferSize ); if (tDataBuff == 0) { status = eMemoryError; break; } pStep1Buff = dsDataBufferAllocate( dsRef, bufferSize ); if (pStep1Buff == 0) { status = eMemoryError; break; } pStepBuff = dsDataBufferAllocate( dsRef, bufferSize ); if (pStepBuff == 0) { status = eMemoryError; break; } tDataBuff->fBufferLength = 0; pStepBuff->fBufferLength = 0; // getpolicy does not require an authentication, but the buffer format // takes one anyway in case the behavior changes. // admin name status = dsUtils.FillAuthBuff( tDataBuff, 3, strlen(inUserID), inUserID, strlen(inPassword), inPassword, strlen(inUserID), inUserID ); if (status != eDSNoErr) break; // get policy pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthGetPolicy ); if ( pAuthType == nil ) { status = eMemoryError; break; } status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStepBuff, nil ); if ( status != eDSNoErr ) break; memcpy(&len, pStepBuff->fBufferData, 4); pStepBuff->fBufferData[len+4] = '\0'; if ( outIsAdmin != NULL && len > 4 ) { char *adminStr = strstr(pStepBuff->fBufferData+4, kIsAdminPolicyStr); if ( adminStr != nil ) { adminStr += strlen(kIsAdminPolicyStr); if ( *adminStr == '1' ) *outIsAdmin = true; } } // clean up if ( pAuthType ) { dsDataBufferDeAllocate( dsRef, pAuthType ); pAuthType = nil; } if ( inVerifyPassword ) { pStepBuff->fBufferLength = 0; // verify password pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthNodeNativeNoClearText ); if ( pAuthType == nil ) { status = eMemoryError; break; } status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStepBuff, nil ); if ( status == eDSNoErr ) break; } } while(false); // clean up if ( tDataBuff ) dsDataBufferDeAllocate( dsRef, tDataBuff ); if ( pStep1Buff ) dsDataBufferDeAllocate( dsRef, pStep1Buff ); if ( pStepBuff ) dsDataBufferDeAllocate( dsRef, pStepBuff ); if ( pAuthType ) dsDataBufferDeAllocate( dsRef, pAuthType ); debugerr( status, "VerifyUserID = %ld\n", status ); return status; } //----------------------------------------------------------------------------- // GetReplicaSetup // // Returns: DS Error Code //----------------------------------------------------------------------------- long GetReplicaSetup( const char *inServerAddress, const char *inUserName, const char *inPassword, char *outRSAPublicKey, char *outRSAPrivateKey, long *outPrivateKeyLen, char **outReplicaList ) { char pwServerNodeStr[256]; tDirReference dsRef = 0; tDataBuffer *tDataBuff = 0; unsigned long len; tDataNode *pAuthType = nil; tDataBuffer *pStep1Buff = nil; long status = eDSAuthFailed; tDirNodeReference nodeRef = 0; tContextData continueData = nil; const long bufferSize = 4096; long replicaListMax = 50 * 700; char *listPtr = nil; char *pos; size_t listSize = 0; DSUtils dsUtils; try { if ( inServerAddress == nil || inUserName == nil || outPrivateKeyLen == nil || inPassword == nil || outRSAPublicKey == nil || outRSAPrivateKey == nil || outReplicaList == nil ) { throw((long)eParameterError); } *outRSAPublicKey = '\0'; *outRSAPrivateKey = '\0'; *outPrivateKeyLen = 0; *outReplicaList = NULL; status = dsUtils.OpenSpecificPasswordServerNode( inServerAddress ); if (status != eDSNoErr) throw(status); dsRef = dsUtils.GetDSRef(); nodeRef = dsUtils.GetCurrentNodeRef(); tDataBuff = dsDataBufferAllocate( dsRef, bufferSize ); if (tDataBuff == NULL) throw((long)eMemoryError); pStep1Buff = dsDataBufferAllocate( dsRef, bufferSize ); if (pStep1Buff == NULL) throw((long)eMemoryError); // reuse <pwServerNodeStr> sprintf( pwServerNodeStr, "%sdsAuthSyncSetupReplica", kDSNativeAuthMethodPrefix ); pAuthType = dsDataNodeAllocateString( dsRef, pwServerNodeStr ); if ( pAuthType == NULL ) throw((long)eMemoryError); status = dsUtils.FillAuthBuff( tDataBuff, 3, strlen(inUserName), inUserName, strlen(inPassword), inPassword, strlen("GET"), "GET" ); if (status != eDSNoErr) throw(status); // dsDoDirNodeAuth status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStep1Buff, nil ); if ( status != eDSNoErr ) throw(status); memcpy(&len, pStep1Buff->fBufferData, 4); if ( len < 4 ) throw((long)eDSAuthResponseBufTooSmall); if ( len > bufferSize - 5 ) throw((long)eDSInvalidBuffFormat); memcpy( outRSAPrivateKey, pStep1Buff->fBufferData + 4, len ); *outPrivateKeyLen = len; pos = pStep1Buff->fBufferData + 4 + len; memcpy(&len, pos, 4); if ( len < 4 ) throw((long)eDSAuthResponseBufTooSmall); if ( len > bufferSize - 5 || len > kPWFileMaxPublicKeyBytes - 1 ) throw((long)eDSInvalidBuffFormat); strlcpy( outRSAPublicKey, pos + 4, len + 1 ); // get the replica list, can use the same auth buffers dsDataBufferDeAllocate( dsRef, pAuthType ); sprintf( pwServerNodeStr, "%sdsAuthListReplicas", kDSNativeAuthMethodPrefix ); pAuthType = dsDataNodeAllocateString( dsRef, pwServerNodeStr ); if ( pAuthType == nil ) throw((long)eMemoryError); // dsDoDirNodeAuth do { status = dsDoDirNodeAuth( nodeRef, pAuthType, true, tDataBuff, pStep1Buff, &continueData ); if ( status != eDSNoErr ) throw( status ); if ( pStep1Buff->fBufferLength < 4 ) throw( (long)eDSInvalidBuffFormat ); memcpy( &len, pStep1Buff->fBufferData, 4 ); if ( len > bufferSize - 4 ) throw( (long)eDSInvalidBuffFormat ); // first time through? if ( *outReplicaList == NULL ) { listSize = (continueData != NULL) ? replicaListMax : (len + 1); *outReplicaList = (char *) malloc( listSize ); if ( *outReplicaList == NULL ) throw( (long)eMemoryError ); listPtr = *outReplicaList; } // time to realloc? if ( ((listPtr + len) - (*outReplicaList)) > replicaListMax ) { char *tempResult; replicaListMax *= 3; // if the new allocation fails, the old pointer is still good and // needs to be freed below in the clean up section tempResult = (char *) realloc( *outReplicaList, replicaListMax ); if ( tempResult == NULL ) throw( (sInt32)eMemoryError ); // restore listPtr to the current position in the buffer listPtr = tempResult + (listPtr - (*outReplicaList)); // change to the new allocation *outReplicaList = tempResult; } strncpy( listPtr, pStep1Buff->fBufferData + 4, len ); listPtr += len; *listPtr = '\0'; } while ( status == eDSNoErr && continueData != NULL ); } catch( long catchErr ) { status = catchErr; } catch(...) { status = -1; } // clean up if ( status != eDSNoErr ) { if ( *outReplicaList != NULL ) { free( *outReplicaList ); *outReplicaList = NULL; } } if ( tDataBuff ) dsDataBufferDeAllocate( dsRef, tDataBuff ); if ( pStep1Buff ) dsDataBufferDeAllocate( dsRef, pStep1Buff ); if ( pAuthType ) dsDataBufferDeAllocate( dsRef, pAuthType ); return status; } #pragma mark - #pragma mark PASSWORD SERVER STATE #pragma mark - //----------------------------------------------------------------------------- // launchdRunning //----------------------------------------------------------------------------- bool LaunchdRunning( unsigned long *outPID ) { pid_t pid = ProcessRunning( "launchd" ); if ( outPID != NULL ) *outPID = pid; return ( pid > 0 ); } //----------------------------------------------------------------------------- // ResumePasswordServer // // Returns: TRUE if the password server had to be launched. //----------------------------------------------------------------------------- bool ResumePasswordServer( void ) { unsigned long pid = 0; unsigned long launchdPID = 0; int retries = 7; FILE *fp = NULL; int result = 0; bool returnCode = false; if ( PasswordServerRunning( &pid ) ) { // (re)save the plist but don't relaunch autoLaunchPasswordServer( true, false ); kill(pid, SIGINFO); } else { returnCode = true; autoLaunchPasswordServer( true, true ); if ( LaunchdRunning( &launchdPID ) ) { // launchd will start the password server, just wait while ( retries-- > 0 && ! PasswordServerRunning( &pid ) ) sleep(1); } if ( ! PasswordServerRunning( &pid ) ) { // watchdog is asleep; fire up the server manually // we don't want watchdog to perpetually launch a second copy autoLaunchPasswordServer( false, true ); debug( "temporarily setting password server to off in launchd for safe manual launch of password server.\n" ); // launch fp = log_popen("/usr/sbin/PasswordService", "r"); if ( fp != NULL ) result = pclose( fp ); debugerr( result, "error starting PasswordService = %d\n", result ); // wait for start if ( result == 0 ) while ( retries-- > 0 && ! PasswordServerRunning( &pid ) ) sleep(1); gNeededToManuallyLaunchPasswordService = true; } // give the server a moment to initialize PasswordServerListening( kMaxPasswordServerWait ); } return returnCode; } //----------------------------------------------------------------------------- // PausePasswordServer //----------------------------------------------------------------------------- void PausePasswordServer( void ) { unsigned long pid = 0; if ( PasswordServerRunning( &pid ) ) kill(pid, SIGHUP); } //----------------------------------------------------------------------------- // StopPasswordServer // // Sets the password server to "off" in watchdog and waits for the server // to quit. //----------------------------------------------------------------------------- bool StopPasswordServer( void ) { //int result = 0; int tries = 30; unsigned long pid = 0; bool returnCode = false; autoLaunchPasswordServer( false, true ); //result = SetServiceState( "pwd", kActionOff, kPasswordServerCmdStr ); //if ( result == 0 ) { while ( PasswordServerRunning( &pid ) && tries-- > 0 ) sleep(1); if ( PasswordServerRunning( &pid ) ) { kill( pid, SIGKILL ); sleep(1); returnCode = ! PasswordServerRunning( &pid ); } else { returnCode = true; } } return returnCode; } //----------------------------------------------------------------------------- // PasswordServerRunning //----------------------------------------------------------------------------- Boolean PasswordServerRunning( unsigned long *outPID ) { pid_t pid = ProcessRunning( "PasswordService" ); if ( outPID != NULL ) *outPID = pid; return ( pid > 0 ); } //----------------------------------------------------------------------------- // PasswordServerListening // // Returns: TRUE if the password server has completed at least one startup //----------------------------------------------------------------------------- Boolean PasswordServerListening( int secondsToWait ) { bool listening = false; int sock = -1; socklen_t structlength; int byteCount; struct sockaddr_in cin; char packetData[64]; fd_set fdset; struct timeval selectTimeout = { 1, 0 }; for ( int ticker = secondsToWait - 1; ticker >= 0; ticker-- ) { // send a ping if ( testconn_udp( "127.0.0.1", "3659", &sock ) == 0 ) { bzero( &cin, sizeof(cin) ); cin.sin_family = AF_INET; cin.sin_addr.s_addr = htonl( INADDR_ANY ); cin.sin_port = htons( 0 ); byteCount = 0; FD_ZERO( &fdset ); FD_SET( sock, &fdset ); if ( select( FD_SETSIZE, &fdset, NULL, NULL, &selectTimeout ) > 0 ) { structlength = sizeof( cin ); byteCount = recvfrom( sock, packetData, sizeof(packetData) - 1, MSG_DONTWAIT, (struct sockaddr *)&cin, &structlength ); } close( sock ); // don't care what the response is, just that we got one if ( byteCount > 0 ) { listening = true; break; } } else { sleep( 1 ); } } return listening; } #pragma mark - #pragma mark RC4 UTILITIES #pragma mark - //------------------------------------------------------------------------------------------ // GetServerBaseInfo // // Returns: 0 == ok, -1 == fail // // The name is for obfuscation. This function really does RC4 decryption on command-line // arguments. //------------------------------------------------------------------------------------------ int GetServerBaseInfo(const char *inArg, char *outArg) { unsigned long buffLen; unsigned char ibuf[1024]; unsigned char obuf[1024]; char out[8]; unsigned char *p; long lval; unsigned long n; int cnt; RC4_KEY keycopy; if ( inArg == NULL || outArg == NULL ) return -1; buffLen = strlen(inArg) + 1; if ( buffLen >= 1024 ) return -1; snprintf((char *)ibuf, sizeof(ibuf), "%s", inArg); memset(out, 0, sizeof(out)); memset(obuf, 0, sizeof(obuf)); cnt=0; n=strlen((char *)ibuf); p=ibuf; while (p < (ibuf + n)) { strncpy(out, (char *)p, 2); out[2] = '\0'; lval=strtol(out, NULL, 16); obuf[cnt++] = lval; p +=2; } memcpy(ibuf, obuf, buffLen); memset(obuf, 0, sizeof(obuf)); memcpy(&keycopy, &gRC4Key, sizeof(RC4_KEY)); RC4(&keycopy,cnt,ibuf,obuf); memcpy( outArg, obuf, cnt ); outArg[cnt] = '\0'; return 0; } //------------------------------------------------------------------------------------------ // GetCmdLinePass // // Returns: exitcode // // utility function to get passwords from the command args //------------------------------------------------------------------------------------------ int GetCmdLinePass( const char *inArg, char **outPass, long *outPassLen ) { long len; char *pwsPass; int result; if ( outPass == NULL ) return EX_SOFTWARE; *outPass = NULL; len = strlen( inArg ); if ( len > 511 ) { return EX_USAGE; } pwsPass = (char *) malloc( len + 1 ); if ( pwsPass == NULL ) return EX_SOFTWARE; if ( len == 128 || len == 256 ) { result = GetServerBaseInfo( inArg, pwsPass ); debugspecial( "result=%ld, inarg=%s, outArg=%s\n", result, inArg, pwsPass); if ( result != 0 ) { debug( kBadEncodingStr ); return EX_DATAERR; } if ( outPassLen != NULL ) *outPassLen = strlen( pwsPass ); } else { strcpy( pwsPass, inArg ); if ( outPassLen != NULL ) *outPassLen = len; } bzero( (char *)inArg, len ); *outPass = pwsPass; return EX_OK; }