Browse Source

Merge branch 'redirect_xss' into bfgminer

Conflicts:
	util.h
Luke Dashjr 11 years ago
parent
commit
56f2ebb486
4 changed files with 187 additions and 1 deletions
  1. 10 1
      miner.c
  2. 1 0
      miner.h
  3. 170 0
      util.c
  4. 6 0
      util.h

+ 10 - 1
miner.c

@@ -2786,6 +2786,14 @@ void pool_set_opaque(struct pool *pool, bool opaque)
 		       pool->pool_no);
 		       pool->pool_no);
 }
 }
 
 
+bool pool_may_redirect_to(struct pool * const pool, const char * const uri)
+{
+	const char *p = strchr(pool->rpc_url, '#');
+	if (unlikely(p && strstr(&p[1], "redirect")))
+		return true;
+	return match_domains(pool->rpc_url, strlen(pool->rpc_url), uri, strlen(uri));
+}
+
 static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 static bool work_decode(struct pool *pool, struct work *work, json_t *val)
 {
 {
 	json_t *res_val = json_object_get(val, "result");
 	json_t *res_val = json_object_get(val, "result");
@@ -8515,7 +8523,7 @@ tryagain:
 
 
 	/* Detect if a http getwork pool has an X-Stratum header at startup,
 	/* Detect if a http getwork pool has an X-Stratum header at startup,
 	 * and if so, switch to that in preference to getwork if it works */
 	 * and if so, switch to that in preference to getwork if it works */
-	if (pool->stratum_url && want_stratum && (pool->has_stratum || stratum_works(pool))) {
+	if (pool->stratum_url && want_stratum && pool_may_redirect_to(pool, pool->stratum_url) && (pool->has_stratum || stratum_works(pool))) {
 		if (!pool->has_stratum) {
 		if (!pool->has_stratum) {
 
 
 		applog(LOG_NOTICE, "Switching pool %d %s to %s", pool->pool_no, pool->rpc_url, pool->stratum_url);
 		applog(LOG_NOTICE, "Switching pool %d %s to %s", pool->pool_no, pool->rpc_url, pool->stratum_url);
@@ -11694,6 +11702,7 @@ int main(int argc, char *argv[])
 		test_cgpu_match();
 		test_cgpu_match();
 		test_intrange();
 		test_intrange();
 		test_decimal_width();
 		test_decimal_width();
+		test_domain_funcs();
 		utf8_test();
 		utf8_test();
 	}
 	}
 
 

+ 1 - 0
miner.h

@@ -1411,6 +1411,7 @@ extern bool _log_curses_only(int prio, const char *datetime, const char *str);
 extern void clear_logwin(void);
 extern void clear_logwin(void);
 extern void logwin_update(void);
 extern void logwin_update(void);
 extern bool pool_tclear(struct pool *pool, bool *var);
 extern bool pool_tclear(struct pool *pool, bool *var);
+extern bool pool_may_redirect_to(struct pool *, const char *uri);
 extern struct thread_q *tq_new(void);
 extern struct thread_q *tq_new(void);
 extern void tq_free(struct thread_q *tq);
 extern void tq_free(struct thread_q *tq);
 extern bool tq_push(struct thread_q *tq, void *data);
 extern bool tq_push(struct thread_q *tq, void *data);

+ 170 - 0
util.c

@@ -1883,6 +1883,173 @@ static char *json_array_string(json_t *val, unsigned int entry)
 	return NULL;
 	return NULL;
 }
 }
 
 
+bool isCalpha(const int c)
+{
+	if (c >= 'A' && c <= 'Z')
+		return true;
+	if (c >= 'a' && c <= 'z')
+		return true;
+	return false;
+}
+
+const char *get_registered_domain(size_t * const out_domainlen, const char * const fqdn, const size_t fqdnlen)
+{
+	const char *s;
+	int dots = 0;
+	
+	for (s = &fqdn[fqdnlen-1]; s >= fqdn; --s)
+	{
+		if (s[0] == '.')
+		{
+			*out_domainlen = fqdnlen - (&s[1] - fqdn);
+			if (++dots >= 2 && *out_domainlen > 5)
+				return &s[1];
+		}
+		else
+		if (!(dots || isCalpha(s[0])))
+		{
+			*out_domainlen = fqdnlen;
+			return fqdn;
+		}
+	}
+	
+	*out_domainlen = fqdnlen;
+	return fqdn;
+}
+
+const char *extract_domain(size_t * const out_domainlen, const char * const uri, const size_t urilen)
+{
+	const char *p = uri, *b, *q, *s;
+	bool alldigit;
+	
+	p = memchr(&p[1], '/', urilen - (&p[1] - uri));
+	if (p)
+	{
+		if (p[-1] == ':')
+		{
+			// part of the URI scheme, ignore it
+			while (p[0] == '/')
+				++p;
+			p = memchr(p, '/', urilen - (p - uri)) ?: &uri[urilen];
+		}
+	}
+	else
+		p = &uri[urilen];
+	
+	s = p;
+	q = memrchr(uri, ':', p - uri);
+	if (q)
+	{
+		alldigit = true;
+		for (q = b = &q[1]; q < p; ++q)
+			if (!isdigit(q[0]))
+			{
+				alldigit = false;
+				break;
+			}
+		if (alldigit && p != b)
+			p = &b[-1];
+	}
+	
+	alldigit = true;
+	for (b = uri; b < p; ++b)
+	{
+		if (b[0] == ':')
+			break;
+		if (alldigit && !isdigit(b[0]))
+			alldigit = false;
+	}
+	if ((b < p && b[0] == ':') && (b == uri || !alldigit))
+		b = &b[1];
+	else
+		b = uri;
+	while (b <= p && b[0] == '/')
+		++b;
+	if (p - b > 1 && b[0] == '[' && p[-1] == ']')
+	{
+		++b;
+		--p;
+	}
+	else
+	if (memchr(b, ':', p - b))
+		p = s;
+	if (p > b && p[-1] == '.')
+		--p;
+	
+	*out_domainlen = p - b;
+	return b;
+}
+
+bool match_domains(const char * const a, const size_t alen, const char * const b, const size_t blen)
+{
+	size_t a_domainlen, b_domainlen;
+	const char *a_domain, *b_domain;
+	a_domain = extract_domain(&a_domainlen, a, alen);
+	a_domain = get_registered_domain(&a_domainlen, a_domain, a_domainlen);
+	b_domain = extract_domain(&b_domainlen, b, blen);
+	b_domain = get_registered_domain(&b_domainlen, b_domain, b_domainlen);
+	if (a_domainlen != b_domainlen)
+		return false;
+	return !strncasecmp(a_domain, b_domain, a_domainlen);
+}
+
+static
+void _test_extract_domain(const char * const expect, const char * const uri)
+{
+	size_t sz;
+	const char * const d = extract_domain(&sz, uri, strlen(uri));
+	if (sz != strlen(expect) || strncasecmp(d, expect, sz))
+		applog(LOG_WARNING, "extract_domain \"%s\" test failed; got \"%.*s\" instead of \"%s\"", uri, sz, d, expect);
+}
+
+static
+void _test_get_regd_domain(const char * const expect, const char * const fqdn)
+{
+	size_t sz;
+	const char * const d = get_registered_domain(&sz, fqdn, strlen(fqdn));
+	if (d == NULL || sz != strlen(expect) || strncasecmp(d, expect, sz))
+		applog(LOG_WARNING, "get_registered_domain \"%s\" test failed; got \"%.*s\" instead of \"%s\"", fqdn, sz, d, expect);
+}
+
+void test_domain_funcs()
+{
+	_test_extract_domain("s.m.eligius.st", "http://s.m.eligius.st:3334");
+	_test_extract_domain("s.m.eligius.st", "http://s.m.eligius.st:3334/abc/abc/");
+	_test_extract_domain("s.m.eligius.st", "http://s.m.eligius.st/abc/abc/");
+	_test_extract_domain("s.m.eligius.st", "http://s.m.eligius.st");
+	_test_extract_domain("s.m.eligius.st", "http:s.m.eligius.st");
+	_test_extract_domain("s.m.eligius.st", "stratum+tcp:s.m.eligius.st");
+	_test_extract_domain("s.m.eligius.st", "stratum+tcp:s.m.eligius.st:3334");
+	_test_extract_domain("s.m.eligius.st", "stratum+tcp://s.m.eligius.st:3334");
+	_test_extract_domain("s.m.eligius.st", "stratum+tcp://s.m.eligius.st:3334///");
+	_test_extract_domain("s.m.eligius.st", "stratum+tcp://s.m.eligius.st.:3334///");
+	_test_extract_domain("s.m.eligius.st", "s.m.eligius.st:3334");
+	_test_extract_domain("s.m.eligius.st", "s.m.eligius.st:3334///");
+	_test_extract_domain("foohost", "foohost:3334");
+	_test_extract_domain("foohost", "foohost:3334///");
+	_test_extract_domain("foohost", "foohost:3334/abc.com//");
+	_test_extract_domain("", "foohost:");
+	_test_extract_domain("3334", "foohost://3334/abc.com//");
+	_test_extract_domain("192.0.2.0", "foohost:192.0.2.0");
+	_test_extract_domain("192.0.2.0", "192.0.2.0:3334");
+	_test_extract_domain("192.0.2.0", "192.0.2.0:3334///");
+	_test_extract_domain("2001:db8::1", "2001:db8::1");
+	_test_extract_domain("2001:db8::1", "http://[2001:db8::1]");
+	_test_extract_domain("2001:db8::1", "http:[2001:db8::1]");
+	_test_extract_domain("2001:db8::1", "http://[2001:db8::1]:42");
+	_test_extract_domain("2001:db8::1", "http://[2001:db8::1]:42/abc//def/ghi");
+	_test_extract_domain("2001:db8::cafe", "http://[2001:db8::cafe]");
+	_test_extract_domain("2001:db8::cafe", "http:[2001:db8::cafe]");
+	_test_extract_domain("2001:db8::cafe", "http://[2001:db8::cafe]:42");
+	_test_extract_domain("2001:db8::cafe", "http://[2001:db8::cafe]:42/abc//def/ghi");
+	_test_get_regd_domain("eligius.st", "s.m.eligius.st");
+	_test_get_regd_domain("eligius.st", "eligius.st");
+	_test_get_regd_domain("foohost.co.uk", "myserver.foohost.co.uk");
+	_test_get_regd_domain("foohost", "foohost");
+	_test_get_regd_domain("192.0.2.0", "192.0.2.0");
+	_test_get_regd_domain("2001:db8::1", "2001:db8::1");
+}
+
 void stratum_probe_transparency(struct pool *pool)
 void stratum_probe_transparency(struct pool *pool)
 {
 {
 	// Request transaction data to discourage pools from doing anything shady
 	// Request transaction data to discourage pools from doing anything shady
@@ -2019,6 +2186,9 @@ static bool parse_reconnect(struct pool *pool, json_t *val)
 	url = __json_array_string(val, 0);
 	url = __json_array_string(val, 0);
 	if (!url)
 	if (!url)
 		url = pool->sockaddr_url;
 		url = pool->sockaddr_url;
+	else
+	if (!pool_may_redirect_to(pool, url))
+		return false;
 
 
 	port_json = json_array_get(val, 1);
 	port_json = json_array_get(val, 1);
 	if (json_is_number(port_json))
 	if (json_is_number(port_json))

+ 6 - 0
util.h

@@ -99,6 +99,7 @@ const char *bfg_json_obj_string(json_t *json, const char *key, const char *fail)
 
 
 extern const char *__json_array_string(json_t *, unsigned int entry);
 extern const char *__json_array_string(json_t *, unsigned int entry);
 
 
+extern bool isCalpha(int);
 static inline
 static inline
 bool isCspace(int c)
 bool isCspace(int c)
 {
 {
@@ -111,6 +112,11 @@ bool isCspace(int c)
 	}
 	}
 }
 }
 
 
+extern const char *get_registered_domain(size_t *out_len, const char *, size_t len);
+extern const char *extract_domain(size_t *out_len, const char *uri, size_t urilen);
+extern bool match_domains(const char *a, size_t alen, const char *b, size_t blen);
+extern void test_domain_funcs();
+
 
 
 enum bfg_gpio_value {
 enum bfg_gpio_value {
 	BGV_LOW   =  0,
 	BGV_LOW   =  0,