From 028f2964ab913cb67fcb42f82ea2d169bc30c2a2 Mon Sep 17 00:00:00 2001
From: Ben Woolley <tautolog@gmail.com>
Date: Tue, 24 Mar 2015 00:39:19 -0700
Subject: [PATCH 07/13] add random entropy to user-agent and accept-language
 headers.

---
 config.def.h |   4 +--
 surf.c       | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 109 insertions(+), 3 deletions(-)

diff --git a/config.def.h b/config.def.h
index dd40848..da0016c 100644
--- a/config.def.h
+++ b/config.def.h
@@ -1,7 +1,5 @@
 /* modifier 0 means no modifier */
-static char *useragent      = "Mozilla/5.0 (X11; U; Unix; en-US) "
-	"AppleWebKit/537.15 (KHTML, like Gecko) Chrome/24.0.1295.0 "
-	"Safari/537.15 Surf/"VERSION;
+static char *useragent      = NULL;
 static char *scriptfile     = "~/.surf/script.js";
 static char *styledir       = "~/.surf/styles/";
 static char *cachefolder    = "~/.surf/cache/";
diff --git a/surf.c b/surf.c
index c91ac95..2cc4ece 100644
--- a/surf.c
+++ b/surf.c
@@ -109,6 +109,7 @@ static int policysel = 0;
 static char *stylefile = NULL;
 static SoupCache *diskcache = NULL;
 
+static void acceptlanguagescramble();
 static void addaccelgroup(Client *c);
 static void beforerequest(WebKitWebView *w, WebKitWebFrame *f,
 		WebKitWebResource *r, WebKitNetworkRequest *req,
@@ -198,7 +199,11 @@ static void setup(void);
 static void sigchld(int unused);
 static void source(Client *c, const Arg *arg);
 static void spawn(Client *c, const Arg *arg);
+static gchar *strentropy();
+static gchar *strlangentropy();
+static int strrand(char *buf, int buflen);
 static void stop(Client *c, const Arg *arg);
+static void useragentscramble(WebKitWebView *view);
 static void titlechange(WebKitWebView *view, GParamSpec *pspec, Client *c);
 static void titlechangeleave(void *a, void *b, Client *c);
 static void toggle(Client *c, const Arg *arg);
@@ -479,6 +484,11 @@ static gboolean
 decidenavigation(WebKitWebView *view, WebKitWebFrame *f, WebKitNetworkRequest *r,
 		WebKitWebNavigationAction *n, WebKitWebPolicyDecision *p,
 		Client *c) {
+	if (!useragent) {
+		useragentscramble(view);
+	}
+	acceptlanguagescramble();
+
 	return FALSE;
 }
 
@@ -965,6 +975,11 @@ newclient(void) {
 	g_object_set(G_OBJECT(settings), "resizable-text-areas",
 			1, NULL);
 
+	if (!useragent) {
+		useragentscramble(c->view);
+	}
+	acceptlanguagescramble();
+
 	/*
 	 * While stupid, CSS specifies that a pixel represents 1/96 of an inch.
 	 * This ensures websites are not unusably small with a high DPI screen.
@@ -1329,6 +1344,99 @@ spawn(Client *c, const Arg *arg) {
 	}
 }
 
+static int
+strrand(char *buf, int buflen) {
+	int fd;
+	int received;
+
+	fd = g_open("/dev/urandom", O_RDONLY, 0);
+	if (fd == -1) {
+		return -1;
+	}
+	received = read(fd, buf, buflen);
+	if (received != buflen) {
+		return -1;
+	}
+	return g_close(fd, NULL);
+}
+
+/* return value must be freed with g_free() */
+static gchar *
+strentropy() {
+	gchar *strname = NULL;
+	char byte, randbuf[256], namebuf[256];
+	int rand_i, name_i;
+
+	if (strrand(randbuf, sizeof (randbuf)) == -1) {
+		return NULL;
+	}
+
+	name_i = 0;
+	for (rand_i = 0; rand_i < sizeof (randbuf) && name_i < sizeof (namebuf); rand_i++) {
+		byte = randbuf[rand_i];
+		if (byte >= ' ' - 5 && byte <= ' ') { /* space AND some characters below, to increase the number of spaces */
+			namebuf[name_i++] = ' ';
+		} else if ((byte >= 'A' && byte <= 'Z') || (byte >= 'a' && byte <= 'z') || (byte >= '0' && byte <= '9')) {
+			namebuf[name_i++] = byte;
+		} else {
+			/* some bytes are skipped, to randomly vary the length */
+		}
+	}
+	namebuf[name_i] = '\0';
+	strname = g_strndup(namebuf, sizeof(namebuf) - 1);
+
+	return strname;
+}
+
+/* return value must be freed with g_free() */
+static gchar *
+strlangentropy() {
+	gchar *strname = NULL;
+	char randbuf[4], langbuf[6];
+
+	if (strrand(randbuf, sizeof (randbuf)) == -1) {
+		return NULL;
+	}
+
+#define randbetween(low, high, randbyte) (low + ((unsigned char) randbyte % (high - low)))
+	langbuf[0] = randbetween('a', 'z', randbuf[0]); 
+	langbuf[1] = randbetween('a', 'z', randbuf[1]);
+	langbuf[2] = '_';
+	langbuf[3] = randbetween('A', 'Z', randbuf[2]);
+	langbuf[4] = randbetween('A', 'Z', randbuf[3]);
+	langbuf[5] = '\0';
+
+	strname = g_strdup(langbuf);
+
+	return strname;
+}
+
+static void
+acceptlanguagescramble() {
+	SoupSession *s = webkit_get_default_session();
+	char *lang = getenv("LANG");
+	char *randlang1 = strlangentropy();
+	char *randlang2 = strlangentropy();
+	char *acceptlanguage;
+
+	if (strlen(lang) >= 5) {
+		acceptlanguage = g_strdup_printf("%5.5s, %s;q=0.9, %s;q=0.8", lang, randlang1, randlang2);
+		g_object_set(G_OBJECT(s), "accept-language", acceptlanguage, NULL);
+	}
+	g_free(randlang1);
+	g_free(randlang2);
+}
+
+static void
+useragentscramble(WebKitWebView *view) {
+	WebKitWebSettings *settings = webkit_web_view_get_settings(view);
+	gchar *ua = strentropy();
+	if (!ua) 
+		ua = " "; /* fallback to blank user-agent -- NULL or "" return a webkit default string that leaks information */
+	g_object_set(G_OBJECT(settings), "user-agent", ua, NULL);
+	g_free(ua);
+}
+
 static void
 eval(Client *c, const Arg *arg) {
 	WebKitWebFrame *frame = webkit_web_view_get_main_frame(c->view);
-- 
2.2.2

