Exemplo n.º 1
0
/**
 * Find latest token structure that is anterior or equal to the remote version.
 */
static const struct tokkey *
find_latest(const version_t *rver)
{
	uint i;
	const struct tokkey *tk;
	const struct tokkey *result = NULL;

	for (i = 0; i < G_N_ELEMENTS(token_keys); i++) {
		tk = &token_keys[i];
		if (version_build_cmp(&tk->ver, rver) > 0)
			break;
		result = tk;
	}

	return result;
}
Exemplo n.º 2
0
/**
 * Validate a base64-encoded version token `tokenb64' of `len' bytes.
 * The `ip' is given only for clock update operations.
 *
 * @returns error code, or TOK_OK if token is valid.
 */
tok_error_t
tok_version_valid(
	const char *version, const char *tokenb64, int len, host_addr_t addr)
{
	time_t now = tm_time();
	time_t stamp;
	uint32 crc;
	const struct tokkey *tk;
	const struct tokkey *rtk;
	const struct tokkey *latest;
	uint idx;
	const char *key;
	SHA1Context ctx;
	char lvldigest[1024];
	char token[TOKEN_VERSION_SIZE];
	struct sha1 digest;
	version_t rver;
	char *end;
	int toklen;
	int lvllen;
	int lvlsize;
	uint i;

	end = strchr(tokenb64, ';');		/* After 25/02/2003 */
	toklen = end ? (end - tokenb64) : len;

	/*
	 * Verify token.
	 */

	if (toklen != TOKEN_BASE64_SIZE)
		return TOK_BAD_LENGTH;

	if (!base64_decode_into(tokenb64, toklen, token, TOKEN_VERSION_SIZE))
		return TOK_BAD_ENCODING;

	stamp = (time_t) peek_be32(&token);

	/*
	 * Use that stamp, whose precision is TOKEN_LIFE, to update our
	 * clock skew if necessary.
	 */

	clock_update(stamp, TOKEN_LIFE, addr);

	if (ABS(stamp - clock_loc2gmt(now)) > TOKEN_CLOCK_SKEW)
		return TOK_BAD_STAMP;

	if (!version_fill(version, &rver))		/* Remote version */
		return TOK_BAD_VERSION;

	tk = find_tokkey_version(&rver, stamp);	/* The keys they used */
	if (tk == NULL)
		return TOK_BAD_KEYS;

	idx = (uchar) token[6] & 0x1f;			/* 5 bits for the index */
	if (idx >= tk->count)
		return TOK_BAD_INDEX;

	key = tk->keys[idx];

	SHA1Reset(&ctx);
	SHA1Input(&ctx, key, strlen(key));
	SHA1Input(&ctx, token, 7);
	SHA1Input(&ctx, version, strlen(version));
	SHA1Result(&ctx, &digest);

	if (0 != memcmp(&token[7], digest.data, SHA1_RAW_SIZE))
		return TOK_INVALID;

	if (version_build_cmp(&rver, &tk->ver) < 0)
		return TOK_OLD_VERSION;

	if (end == NULL)
		return TOK_MISSING_LEVEL;

	latest = find_latest(&rver);
	if (latest == NULL)						/* Unknown in our key set */
		return TOK_OLD_VERSION;

	/*
	 * Verify build.
	 *
	 * Build numbers were emitted when we switched to SVN on 2006-08-26
	 * and stopped being a monotonous increasing function when we switched
	 * to git on 2011-09-11.
	 */

	if (
		rver.timestamp >= 1156543200 &&		/* 2006-08-26 */
		rver.timestamp < GIT_SWITCH			/* 2011-09-11 */
	) {
		if (0 == rver.build)
			return TOK_MISSING_BUILD;
		if (rver.build < latest->ver.build)
			return TOK_WRONG_BUILD;
	}

	/*
	 * Verify level.
	 */

	lvllen = len - toklen - 2;				/* Forget about "; " */
	end += 2;								/* Skip "; " */

	if (UNSIGNED(lvllen) >= sizeof(lvldigest) || lvllen <= 0)
		return TOK_BAD_LEVEL_LENGTH;

	if (lvllen & 0x3)
		return TOK_BAD_LEVEL_LENGTH;

	lvllen = base64_decode_into(end, lvllen, lvldigest, sizeof(lvldigest));

	if (lvllen == 0 || (lvllen & 0x1))
		return TOK_BAD_LEVEL_ENCODING;

	g_assert(lvllen >= 2);
	g_assert((lvllen & 0x1) == 0);

	/*
	 * Only check the highest keys we can check.
	 */

	lvllen /= 2;							/* # of keys held remotely */
	lvlsize = G_N_ELEMENTS(token_keys) - (tk - token_keys);
	lvlsize = MIN(lvllen, lvlsize);

	g_assert(lvlsize >= 1);

	rtk = tk + (lvlsize - 1);				/* Keys at that level */

	crc = crc32_update(0, token, TOKEN_VERSION_SIZE);
	crc = tok_crc(crc, rtk);

	lvlsize--;								/* Move to 0-based offset */

	if (peek_be16(&lvldigest[2*lvlsize]) != crc)
		return TOK_INVALID_LEVEL;

	for (i = 0; i < G_N_ELEMENTS(token_keys); i++) {
		rtk = &token_keys[i];
		if (rtk->ver.timestamp > rver.timestamp) {
			rtk--;							/* `rtk' could not exist remotely */
			break;
		}
	}

	if (lvlsize < rtk - tk)
		return TOK_SHORT_LEVEL;

	return TOK_OK;
}
Exemplo n.º 3
0
/**
 * Check version of servent, and if it's a gtk-gnutella more recent than we
 * are, record that fact and change the status bar.
 *
 * The `addr' is being passed solely for the tok_version_valid() call.
 *
 * @returns TRUE if we properly checked the version, FALSE if we got something
 * looking as gtk-gnutella but which failed the token-based sanity checks.
 */
bool
version_check(const char *str, const char *token, const host_addr_t addr)
{
	version_t their_version;
	version_ext_t their_version_ext;
	version_t *target_version;
	int cmp;
	const char *version;
	const char *end;
	bool extended;

	if (!version_parse(str, &their_version, &end))
		return TRUE;			/* Not gtk-gnutella, or unparseable */

	/*
	 * Check for extended version information (git commit ID, dirty status).
	 */

	ZERO(&their_version_ext);
	extended = version_ext_parse(end, &their_version_ext);
	if (!extended) {
		/* Structure could have been partially filled */
		ZERO(&their_version_ext);
	}

	/*
	 * Is their version a development one, or a release?
	 */

	if (their_version.tag == 'u')
		target_version = &last_dev_version;
	else
		target_version = &last_rel_version;

	cmp = version_cmp(target_version, &their_version);

	if (GNET_PROPERTY(version_debug) > 1)
		version_dump(str, &their_version,
			cmp == 0 ? "=" :
			cmp > 0 ? "-" : "+");

	/*
	 * Check timestamp.
	 */

	version_stamp(str, &their_version);
	their_version_ext.version = their_version;		/* Struct copy */

	if (GNET_PROPERTY(version_debug) > 3)
		g_debug("VERSION time=%d", (int) their_version.timestamp);

	/*
	 * If version claims something older than TOKEN_START_DATE, then
	 * there must be a token present.
	 */

	if (delta_time(their_version.timestamp, 0) >= TOKEN_START_DATE) {
		tok_error_t error;

		if (token == NULL) {
            if (GNET_PROPERTY(version_debug)) {
                g_debug("got GTKG vendor string \"%s\" without token!", str);
            }
			return FALSE;	/* Can't be correct */
		}

		error = tok_version_valid(str, token, strlen(token), addr);

		/*
		 * Unfortunately, if our token has expired, we can no longer
		 * validate the tokens of the remote peers, since they are using
		 * a different set of keys.
		 *
		 * This means an expired GTKG will blindly trust well-formed remote
		 * tokens at face value.  But it's their fault, they should not run
		 * an expired version!
		 *		--RAM, 2005-12-21
		 */

		if (error == TOK_BAD_KEYS && tok_is_ancient(tm_time()))
			error = TOK_OK;		/* Our keys have expired, cannot validate */

		if (error != TOK_OK) {
            if (GNET_PROPERTY(version_debug)) {
                g_debug("vendor string \"%s\" [%s] has wrong token "
                    "\"%s\": %s ", str, host_addr_to_string(addr), token,
                    tok_strerror(error));
            }
			return FALSE;
		}

		/*
		 * OK, so now we know we can "trust" this version string as being
		 * probably genuine.  It makes sense to extract version information
		 * out of it.
		 */
	}

	if (cmp > 0)			/* We're more recent */
		return TRUE;

	/*
	 * If timestamp is greater and we were comparing against a stable
	 * release, and cmp == 0, then this means an update in SVN about
	 * a "released" version, probably alpha/beta.
	 */

	if (
		cmp == 0 &&
		(delta_time(their_version.timestamp, target_version->timestamp) > 0
			|| their_version.build > target_version->build) &&
		target_version == &last_rel_version
	) {
		if (GNET_PROPERTY(version_debug) > 3)
			g_debug("VERSION is a SVN update of a release");

		if (version_build_cmp(&last_dev_version, &their_version) > 0) {
			if (GNET_PROPERTY(version_debug) > 3)
				g_debug("VERSION is less recent than latest dev we know");
			return TRUE;
		}
		target_version = &last_dev_version;
	}

	/*
	 * Their version is more recent, but is unstable -- only continue if
	 * our version is also unstable.
	 */

	if (cmp < 0 && their_version.tag == 'u' && our_version.tag != 'u')
		return TRUE;

	if (
		delta_time(their_version.timestamp, target_version->timestamp) < 0 ||
		their_version.build <= target_version->build
	)
		return TRUE;

	if (
		delta_time(their_version.timestamp, our_version.timestamp) == 0 &&
		their_version.build <= our_version.build
	)
		return TRUE;

	/*
	 * We found a more recent version than the last version seen.
	 */

	if (GNET_PROPERTY(version_debug) > 1)
		g_debug("more recent %s VERSION \"%s\"",
			target_version == &last_dev_version ? "dev" : "rel", str);

	*target_version = their_version;		/* struct copy */

	/*
	 * Signal new version to user.
	 *
	 * Unless they run a development version, don't signal development
	 * updates to them: they're probably not interested.
	 */

	version =  version_ext_str(&their_version_ext, FALSE);	/* No OS name */

	g_message("more recent %s version of gtk-gnutella: %s",
		target_version == &last_dev_version ? "development" : "released",
		version);

	if (target_version == &last_rel_version)
		version_new_found(version, TRUE);
	else if (our_version.tag == 'u')
		version_new_found(version, FALSE);

	return TRUE;
}