xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
<root> / inc / token.inc.php (ecf6f7eeebc958c17ae0c5f86b0499b01de0f1ce) (5,153B) (mode 100644) [raw]
<?php
require_once($INC . "/util.inc.php");
require_once($INC . "/log.inc.php");
require_once($INC . "/sql.inc.php");
require_once($INC . "/prof.inc.php");

$rg_token_error = "";

function rg_token_set_error($str)
{
	global $rg_token_error;
	$rg_token_error = $str;
	rg_log($str);
}

function rg_token_error()
{
	global $rg_token_error;
	return $rg_token_error;
}

/*
 * Delete a token
 */
function rg_token_delete($db, $sid, $token)
{
	rg_prof_start("token_delete");
	rg_log_enter("token_delete: sid=$sid token=$token");

	$ret = array();
	$ret['ok'] = 0;
	while (1) {
		$params = array("sid" => $sid, "token" => $token);
		$add_token = "";
		if (!empty($token))
			$add_token = " AND token = @@token@@";

		$sql = "DELETE FROM tokens"
			. " WHERE sid = @@sid@@"
			. $add_token;
		$res = rg_sql_query_params($db, $sql, $params);
		if ($res === FALSE) {
			rg_token_set_error("cannot delete token (" . rg_sql_error() . ")");
			break;
		}
		rg_sql_free_result($res);

		$ret['ok'] = 1;
		break;
	}

	rg_log_exit();
	rg_prof_end("token_delete");
	return $ret;
}

/*
 * This function will get the master key from db
 */
function rg_token_get_master($db)
{
	rg_prof_start("token_get_master");
	rg_log_enter("token_get_master");

	$ret = FALSE;
	while (1) {
		$key = rg_state_get($db, "token_key");
		if ($key === FALSE) {
			rg_token_set_error("cannot get token_key:"
				. " " . rg_state_error());
			break;
		}

		if (empty($key)) {
			$key = rg_id(32);
			$r = rg_state_set($db, "token_key", $key);
			if ($r === FALSE) {
				rg_token_set_error("cannot set state:"
					. " " . rg_state_error());
				break;
			}
		}

		$ret = $key;
		break;
	}

	rg_log_exit();
	rg_prof_end("token_get_master");
	return $ret;
}

/*
 * Returns TRUE if the token is valid
 */
function rg_token_valid($db, $rg)
{
	rg_prof_start("token_valid");
	rg_log_enter("token_valid: sid=" . $rg['sid'] . " token=" . $rg['token']
		. " ua=" . $rg['ua']);

	$ret = FALSE;
	while (1) {
		$ua_hash = substr(sha1($rg['ua']), 0, 8);

		if (strncmp($rg['sid'], "X", 1) == 0) {
			// We have a pre-login session.
			if (strlen($rg['token']) != 32) {
				rg_token_set_error("invalid token");
				break;
			}

			$key = rg_token_get_master($db);
			if ($key === FALSE)
				break;

			$rand = substr($rg['token'], 0, 16);
			$sign = substr($rg['token'], 16, 16);

			$data = $rand . $rg['sid'] . $ua_hash;
			$hash = hash_hmac("sha1", $data, $key);
			if ($hash === FALSE) {
				rg_token_set_error("cannot compute hmac");
				break;
			}

			$hash = substr($hash, 0, 16);
			if (strcmp($sign, $hash) != 0) {
				rg_log("DEBUG: sign=$sign != hash=$hash	data=$data");
				rg_token_set_error("token invalid");
				break;
			}

			$ret = TRUE;
			break;
		}

		$rand = substr($rg['token'], 0, 16);
		$search = $rand . $ua_hash;
		$params = array("sid" => $rg['sid'], "token" => $search);
		$sql = "UPDATE tokens SET used = 1"
			. " WHERE token = @@token@@"
			. " AND sid = @@sid@@"
			. " AND used = 0";
		$res = rg_sql_query_params($db, $sql, $params);
		if ($res === FALSE) {
			rg_token_set_error("cannot get token (" . rg_sql_error() . ")");
			break;
		}

		$rows = rg_sql_affected_rows($res);
		rg_sql_free_result($res);
		if ($rows == 0)
			break;

		$ret = TRUE;
		break;
	}

	if ($ret === FALSE)
		rg_security_violation_no_exit("invalid token");

	rg_log_exit();
	rg_prof_end("token_valid");
	return $ret;
}

/*
 * Insert a token
 */
function rg_token_insert($db, $sid, $token)
{
	rg_prof_start("token_insert");
	rg_log_enter("token_insert: sid=$sid token=$token");

	$ret = array();
	$ret['ok'] = 0;
	while (1) {
		$now = time();

		$params = array("sid" => $sid,
			"token" => $token,
			"expire" => $now + 24 * 3600);
		$sql = "INSERT INTO tokens (sid, token, expire)"
			. " VALUES (@@sid@@, @@token@@, @@expire@@)";
		$res = rg_sql_query_params($db, $sql, $params);
		if ($res === FALSE) {
			rg_token_set_error("cannot insert token (" . rg_sql_error() . ")!");
			break;
		}

		$ret['ok'] = 1;
		break;
	}

	rg_log_exit();
	rg_prof_end("token_insert");
	return $ret;
}

/*
 * Returns a token to be used on a form/url
 * We generate only one per form, but multiple per session.
 */
$rg_token = FALSE;
function rg_token_get($db, $rg)
{
	global $rg_token;

	rg_log_enter("token_get: sid=" . $rg['sid']);

	$ret = FALSE;
	while (1) {
		if (empty($rg['sid']))
			break;

		if ($rg_token !== FALSE) {
			$ret = $rg_token;
			break;
		}

		$rand = rg_id(16);
		$ua_hash = substr(sha1($rg['ua']), 0, 8);

		if (strncmp($rg['sid'], "X", 1) == 0) {
			// we have a pre-login session
			$key = rg_token_get_master($db);
			if ($key === FALSE)
				break;

			$data = $rand . $rg['sid'] . $ua_hash;
			$sign = hash_hmac("sha1", $data, $key);
			if ($sign === FALSE) {
				rg_token_set_error("cannot compute hmac");
				break;
			}
			$sign = substr($sign, 0, 16);
			$token = $rand . $sign;

			rg_log("DEBUG: sign=$sign rand=$rand sid=" . $rg['sid'] . " ua_hash=$ua_hash");
		} else {
			$token = $rand . $ua_hash;
			$r = rg_token_insert($db, $rg['sid'], $token);
			if ($r['ok'] != 1)
				break;
		}

		$rg_token = $token;
		$ret = $token;
		break;
	}

	rg_log_exit();
	return $ret;
}

?>
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master