aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/lib/api.php88
-rw-r--r--engine/lib/sessions.php6
-rw-r--r--services/api/rest.php9
3 files changed, 81 insertions, 22 deletions
diff --git a/engine/lib/api.php b/engine/lib/api.php
index af299b85f..aedaae878 100644
--- a/engine/lib/api.php
+++ b/engine/lib/api.php
@@ -284,6 +284,32 @@
}
/**
+ * This function analyses all expected parameters for a given method, returning them in an associated array from
+ * input.
+ *
+ * This ensures that they are sanitised and that no superfluous commands are registered. It also means that
+ * hmacs work through the page handler.
+ *
+ * @param string $method The method
+ * @return Array containing commands and values, including method and api
+ */
+ function get_parameters_for_method($method)
+ {
+ global $CONFIG, $METHODS;
+
+ $method = sanitise_string($method);
+ $sanitised = array();
+
+ foreach ($CONFIG->input as $k => $v)
+ {
+ if ((isset($METHODS[$method]['parameters'][$k])) || ($k == 'auth_token') || ($k == 'method'))
+ $sanitised[$k] = get_input($k); // Make things go through the sanitiser
+ }
+
+ return $sanitised;
+ }
+
+ /**
* Obtain a token for a user.
*
* @param string $username The username
@@ -640,14 +666,14 @@
function cache_hmac_check_replay($hmac)
{
$cache = new ElggHMACCache(90000); // cache lifetime is 25 hours (see time window in get_and_validate_api_headers() )
-
+
if (!$cache->load($hmac))
- {
+ {
$cache->save($hmac, $hmac);
-
+
return false;
}
-
+
return true;
}
@@ -716,7 +742,7 @@
{
$result = new stdClass;
- $result->method = $_SERVER['REQUEST_METHOD'];
+ $result->method = get_call_method();
if (($result->method != "GET") && ($result->method!= "POST")) // Only allow these methods
throw new APIException(elgg_echo('APIException:NotGetOrPost'));
@@ -738,7 +764,7 @@
if (($result->time<(microtime(true)-86400.00)) || ($result->time>(microtime(true)+86400.00))) // Basic timecheck, think about making this smaller if we get loads of users and the cache gets really big.
throw new APIException(elgg_echo('APIException:TemporalDrift'));
- $result->get_variables = $_SERVER['QUERY_STRING'];
+ $result->get_variables = get_parameters_for_method(get_input('method')); //$_SERVER['QUERY_STRING'];
if ($result->get_variables == "")
throw new APIException(elgg_echo('APIException:NoQueryString'));
@@ -791,15 +817,16 @@
$validated_userid = validate_user_token($CONFIG->site_id, $token);
- if ($validated_userid) {
+ if ($validated_userid) {
$u = get_entity($validated_userid);
if (!$u) return false; // Could we get the user?
if (!login($u)) return false; // Fail if we couldn't log the user in (likely means they were banned).
+
}
- if ((!$METHODS[$method]["require_auth_token"]) || ($validated_userid) || (isloggedin()))
+ if ((!$METHODS[$method]["require_auth_token"]) || ($validated_userid) || (isloggedin())) {
return true;
- else
+ } else
throw new SecurityException(elgg_echo('SecurityException:AuthTokenExpired'), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
return false;
@@ -845,14 +872,20 @@
// Get the secret key
$secret_key = $api_user->secret;
+ // Serialise parameters
+ $encoded_params = array();
+ foreach ($api_header->get_variables as $k => $v)
+ $encoded_params[] = urlencode($k).'='.urlencode($v);
+ $params = implode('&', $encoded_params);
+
// Validate HMAC
$hmac = calculate_hmac($api_header->hmac_algo,
$api_header->time,
$api_header->api_key,
$secret_key,
- $api_header->get_variables,
+ $params,
$api_header->method == 'POST' ? $api_header->posthash : "");
-
+
if (strcmp(
$api_header->hmac,
$hmac
@@ -861,6 +894,7 @@
// Now make sure this is not a replay
if (!cache_hmac_check_replay($hmac))
{
+
// Validate post data
if ($api_header->method=="POST")
{
@@ -878,7 +912,7 @@
throw new SecurityException(elgg_echo('SecurityException:DupePacket'));
}
else
- throw new SecurityException("HMAC is invalid. {$api_header->hmac} != [calc]$hmac = {$api_header->hmac_algo}(**SECRET KEY**, time:{$api_header->time}, apikey:{$api_header->api_key}, get_vars:{$api_header->get_variables}" . ($api_header->method=="POST"? "posthash:$api_header->posthash}" : ")"));
+ throw new SecurityException("HMAC is invalid. {$api_header->hmac} != [calc]$hmac = {$api_header->hmac_algo}(**SECRET KEY**, time:{$api_header->time}, apikey:{$api_header->api_key}, get_vars:{$params}" . ($api_header->method=="POST"? "posthash:$api_header->posthash}" : ")"));
}
else
throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'),ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
@@ -886,6 +920,25 @@
return false;
}
+ /**
+ * A bit of a hack. Basically, this combines session and hmac, so that one of them must evaluate to true in order
+ * to proceed.
+ *
+ * This ensures that this and auth_token are evaluated separately.
+ *
+ * @param unknown_type $credentials
+ */
+ function pam_auth_session_or_hmac($credentials = NULL)
+ {
+ if (pam_auth_session($credentials))
+ return true;
+
+ if (pam_auth_hmac($credentials))
+ return true;
+
+ return false;
+ }
+
// Client api functions ///////////////////////////////////////////////////////////////////
$APICLIENT_LAST_CALL = NULL;
@@ -921,7 +974,7 @@
*/
function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream')
{
- global $APICLIENT_LAST_CALL, $APICLIENT_LAST_CALL_RAW, $APICLIENT_LAST_ERROR;
+ global $APICLIENT_LAST_CALL, $APICLIENT_LAST_CALL_RAW, $APICLIENT_LAST_ERROR, $CONFIG;
$headers = array();
$encoded_params = array();
@@ -937,10 +990,13 @@
// Time
$time = microtime(true);
- // URL encode all the parameters
+ // URL encode all the parameters, ensuring auth_token (if present) is at the end!
foreach ($call as $k => $v){
- $encoded_params[] = urlencode($k).'='.urlencode($v);
+ if ($k!='auth_token')
+ $encoded_params[] = urlencode($k).'='.urlencode($v);
}
+ if ($call['auth_token'])
+ $encoded_params[] = urlencode('auth_token').'='.urlencode($call['auth_token']);
$params = implode('&', $encoded_params);
@@ -986,6 +1042,8 @@
$context = stream_context_create($opts);
// Send the query and get the result and decode.
+ if ((isset($CONFIG->debug)) && ($CONFIG->debug))
+ error_log("APICALL: $url");
$APICLIENT_LAST_CALL_RAW = file_get_contents($url, false, $context);
$APICLIENT_LAST_CALL = unserialize($APICLIENT_LAST_CALL_RAW);
diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php
index 3dd9ac9e1..39e564ac8 100644
--- a/engine/lib/sessions.php
+++ b/engine/lib/sessions.php
@@ -101,8 +101,10 @@
*/
function login(ElggUser $user, $persistent = false) {
- global $CONFIG;
-
+ global $CONFIG;
+error_log('*****************************************');
+error_log(print_r($_SESSION, true));
+error_log('*****************************************');
$_SESSION['user'] = $user;
$_SESSION['guid'] = $user->getGUID();
$_SESSION['id'] = $_SESSION['guid'];
diff --git a/services/api/rest.php b/services/api/rest.php
index cee7ebc43..c6632cbc7 100644
--- a/services/api/rest.php
+++ b/services/api/rest.php
@@ -29,21 +29,20 @@
throw new SecurityException(elgg_echo('SecurityException:APIAccessDenied'));
// Register some default PAM methods, plugins can add their own
- register_pam_handler('pam_auth_session');
- register_pam_handler('pam_auth_hmac');
+ register_pam_handler('pam_auth_session_or_hmac'); // Command must either be authenticated by a hmac or the user is already logged in
register_pam_handler('pam_auth_usertoken', 'required'); // Either token present and valid OR method doesn't require one.
register_pam_handler('pam_auth_anonymous_method'); // Support anonymous functions
// Get parameter variables
$method = get_input('method');
$result = null;
-
+
// Authenticate session
if (pam_authenticate())
{
// Authenticated somehow, now execute.
- $token = "";
- $params = $CONFIG->input;// Use $CONFIG->input instead of $_REQUEST since this is called by the pagehandler
+ $token = "";
+ $params = get_parameters_for_method($method); // Use $CONFIG->input instead of $_REQUEST since this is called by the pagehandler
if (isset($params['auth_token'])) $token = $params['auth_token'];
$result = execute_method($method, $params, $token);