Ejemplo n.º 1
0
 function prepare($args)
 {
     StatusNet::setApi(true);
     // Send smaller error pages
     parent::prepare($args);
     if ($_SERVER['REQUEST_METHOD'] != 'POST') {
         $this->clientError(_m('This method requires a POST.'));
     }
     if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/magic-envelope+xml') {
         $this->clientError(_m('Salmon requires application/magic-envelope+xml'));
     }
     $xml = file_get_contents('php://input');
     // Check the signature
     $salmon = new Salmon();
     if (!$salmon->verifyMagicEnv($xml)) {
         common_log(LOG_DEBUG, "Salmon signature verification failed.");
         $this->clientError(_m('Salmon signature verification failed.'));
     } else {
         $magic_env = new MagicEnvelope();
         $env = $magic_env->parse($xml);
         $xml = $magic_env->unfold($env);
     }
     $dom = DOMDocument::loadXML($xml);
     if ($dom->documentElement->namespaceURI != Activity::ATOM || $dom->documentElement->localName != 'entry') {
         common_log(LOG_DEBUG, "Got invalid Salmon post: {$xml}");
         $this->clientError(_m('Salmon post must be an Atom entry.'));
     }
     $this->act = new Activity($dom->documentElement);
     return true;
 }
Ejemplo n.º 2
0
 protected function prepare(array $args = array())
 {
     GNUsocial::setApi(true);
     // Send smaller error pages
     parent::prepare($args);
     if (!isset($_SERVER['CONTENT_TYPE'])) {
         // TRANS: Client error. Do not translate "Content-type"
         $this->clientError(_m('Salmon requires a Content-type header.'));
     }
     $envxml = null;
     switch ($_SERVER['CONTENT_TYPE']) {
         case 'application/magic-envelope+xml':
             $envxml = file_get_contents('php://input');
             break;
         case 'application/x-www-form-urlencoded':
             $envxml = Magicsig::base64_url_decode($this->trimmed('xml'));
             break;
         default:
             // TRANS: Client error. Do not translate the quoted "application/[type]" strings.
             $this->clientError(_m('Salmon requires "application/magic-envelope+xml". For Diaspora we also accept "application/x-www-form-urlencoded" with an "xml" parameter.', 415));
     }
     try {
         if (empty($envxml)) {
             throw new ClientException('No magic envelope supplied in POST.');
         }
         $magic_env = new MagicEnvelope($envxml);
         // parse incoming XML as a MagicEnvelope
         $entry = $magic_env->getPayload();
         // Not cryptographically verified yet!
         $this->activity = new Activity($entry->documentElement);
         if (empty($this->activity->actor->id)) {
             common_log(LOG_ERR, "broken actor: " . var_export($this->activity->actor->id, true));
             common_log(LOG_ERR, "activity with no actor: " . var_export($this->activity, true));
             // TRANS: Exception.
             throw new Exception(_m('Received a salmon slap from unidentified actor.'));
         }
         // ensureProfiles sets $this->actor and $this->oprofile
         $this->ensureProfiles();
     } catch (Exception $e) {
         common_debug('Salmon envelope parsing failed with: ' . $e->getMessage());
         $this->clientError($e->getMessage());
     }
     // Cryptographic verification test
     if (!$magic_env->verify($this->actor)) {
         common_log(LOG_DEBUG, "Salmon signature verification failed.");
         // TRANS: Client error.
         $this->clientError(_m('Salmon signature verification failed.'));
     }
     return true;
 }
Ejemplo n.º 3
0
 /**
  * Sign and post the given Atom entry as a Salmon message.
  *
  * Side effects: may generate a keypair on-demand for the given user,
  * which can be very slow on some systems (like those without php5-gmp).
  *
  * @param string $endpoint_uri
  * @param string $xml string representation of payload
  * @param Profile $user profile whose keys we sign with (must be a local user)
  * @return boolean success
  */
 public static function post($endpoint_uri, $xml, Profile $actor, Profile $target = null)
 {
     if (empty($endpoint_uri)) {
         common_debug('No endpoint URI for Salmon post to ' . $actor->getUri());
         return false;
     }
     try {
         $magic_env = MagicEnvelope::signAsUser($xml, $actor->getUser());
     } catch (Exception $e) {
         common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
         return false;
     }
     // $target is so far only used in Diaspora, so it can be null
     if (Event::handle('SalmonSlap', array($endpoint_uri, $magic_env, $target))) {
         return false;
         //throw new ServerException('Could not distribute salmon slap as no plugin completed the event.');
     }
     return true;
 }
Ejemplo n.º 4
0
 /**
  * Sign and post the given Atom entry as a Salmon message.
  *
  * Side effects: may generate a keypair on-demand for the given user,
  * which can be very slow on some systems (like those without php5-gmp).
  *
  * @param string $endpoint_uri
  * @param string $xml string representation of payload
  * @param User $user local user profile whose keys we sign with
  * @return boolean success
  */
 public static function post($endpoint_uri, $xml, User $user)
 {
     if (empty($endpoint_uri)) {
         common_debug('No endpoint URI for Salmon post to ' . $user->getUri());
         return false;
     }
     try {
         $magic_env = MagicEnvelope::signAsUser($xml, $user);
         $envxml = $magic_env->toXML();
     } catch (Exception $e) {
         common_log(LOG_ERR, "Salmon unable to sign: " . $e->getMessage());
         return false;
     }
     $headers = array('Content-Type: application/magic-envelope+xml');
     try {
         $client = new HTTPClient();
         $client->setBody($envxml);
         $response = $client->post($endpoint_uri, $headers);
     } catch (HTTP_Request2_Exception $e) {
         common_log(LOG_ERR, "Salmon post to {$endpoint_uri} failed: " . $e->getMessage());
         return false;
     }
     // Diaspora wants a slightly different formatting on the POST (other Content-type, so body needs "xml=")
     if ($response->getStatus() === 422) {
         common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. Diaspora? Will try again! Body: %s', $user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
         $headers = array('Content-Type: application/x-www-form-urlencoded');
         $client->setBody('xml=' . Magicsig::base64_url_encode($envxml));
         $response = $client->post($endpoint_uri, $headers);
     }
     // 200 OK is the best response
     // 202 Accepted is what we get from Diaspora for example
     if (!in_array($response->getStatus(), array(200, 202))) {
         common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s', $user->id, $endpoint_uri, $response->getStatus(), $response->getBody()));
         return false;
     }
     // Success!
     return true;
 }
Ejemplo n.º 5
0
    exit(1);
}
$notice_id = get_option_value('--notice');
$notice = Notice::getKV('id', $notice_id);
$profile = $notice->getProfile();
$entry = $notice->asAtomEntry(true);
echo "== Original entry ==\n\n";
print $entry;
print "\n\n";
$magic_env = MagicEnvelope::signAsUser($entry, $profile->getUser());
$envxml = $magic_env->toXML();
echo "== Signed envelope ==\n\n";
print $envxml;
print "\n\n";
echo "== Testing local verification ==\n\n";
$magic_env = new MagicEnvelope($envxml);
$activity = new Activity($magic_env->getPayload()->documentElement);
$actprofile = Profile::fromUri($activity->actor->id);
$ok = $magic_env->verify($actprofile);
if ($ok) {
    print "OK\n\n";
} else {
    print "FAIL\n\n";
}
if (have_option('--verify')) {
    $url = 'http://www.madebymonsieur.com/ostatus_discovery/magic_env/validate/';
    echo "== Testing remote verification ==\n\n";
    print "Sending for verification to {$url} ...\n";
    $client = new HTTPClient();
    $response = $client->post($url, array(), array('magic_env' => $envxml));
    print $response->getStatus() . "\n\n";
Ejemplo n.º 6
0
 /**
  * Encode the given string as a signed MagicEnvelope XML document,
  * using the keypair for the given local user profile. We can of
  * course not sign a remote profile's slap, since we don't have the
  * private key.
  *
  * Side effects: will create and store a keypair on-demand if one
  * hasn't already been generated for this user. This can be very slow
  * on some systems.
  *
  * @param string $text XML fragment to sign, assumed to be Atom
  * @param User $user User who cryptographically signs $text
  *
  * @return MagicEnvelope object complete with signature
  *
  * @throws Exception on bad profile input or key generation problems
  */
 public static function signAsUser($text, User $user)
 {
     // Find already stored key
     $magicsig = Magicsig::getKV('user_id', $user->id);
     if (!$magicsig instanceof Magicsig) {
         $magicsig = Magicsig::generate($user);
     }
     assert($magicsig instanceof Magicsig);
     assert($magicsig->privateKey instanceof Crypt_RSA);
     $magic_env = new MagicEnvelope();
     $magic_env->signMessage($text, 'application/atom+xml', $magicsig);
     return $magic_env;
 }
Ejemplo n.º 7
0
 /**
  * Encode the given string as a signed MagicEnvelope XML document,
  * using the keypair for the given local user profile. We can of
  * course not sign a remote profile's slap, since we don't have the
  * private key.
  *
  * Side effects: will create and store a keypair on-demand if one
  * hasn't already been generated for this user. This can be very slow
  * on some systems.
  *
  * @param string $text XML fragment to sign, assumed to be Atom
  * @param User $user User who cryptographically signs $text
  *
  * @return MagicEnvelope object complete with signature
  *
  * @throws Exception on bad profile input or key generation problems
  */
 public static function signAsUser($text, User $user)
 {
     $magic_env = new MagicEnvelope(null, $user->getProfile());
     $magic_env->signMessage($text, 'application/atom+xml');
     return $magic_env;
 }
Ejemplo n.º 8
0
 public function verifyMagicEnv($text)
 {
     $magic_env = new MagicEnvelope();
     $env = $magic_env->parse($text);
     return $magic_env->verify($env);
 }
Ejemplo n.º 9
0
 /**
  * Test that MagicEnvelope builds the correct plaintext for signing.
  * @dataProvider provider
  */
 public function testSignatureText(MagicEnvelope $env, $expected)
 {
     $text = $env->signingText();
     $this->assertEquals($expected, $text, "'{$text}' should be '{$expected}'");
 }
Ejemplo n.º 10
0
 public function onSalmonSlap($endpoint_uri, MagicEnvelope $magic_env, Profile $target = null)
 {
     $envxml = $magic_env->toXML($target);
     $headers = array('Content-Type: application/magic-envelope+xml');
     try {
         $client = new HTTPClient();
         $client->setBody($envxml);
         $response = $client->post($endpoint_uri, $headers);
     } catch (HTTP_Request2_Exception $e) {
         common_log(LOG_ERR, "Salmon post to {$endpoint_uri} failed: " . $e->getMessage());
         return false;
     }
     if ($response->getStatus() === 422) {
         common_debug(sprintf('Salmon (from profile %d) endpoint %s returned status %s. We assume it is a Diaspora seed; will adapt and try again if that plugin is enabled!', $magic_env->getActor()->getID(), $endpoint_uri, $response->getStatus()));
         return true;
     }
     // 200 OK is the best response
     // 202 Accepted is what we get from Diaspora for example
     if (!in_array($response->getStatus(), array(200, 202))) {
         common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s', $magic_env->getActor()->getID(), $endpoint_uri, $response->getStatus(), $response->getBody()));
         return true;
     }
     // Since we completed the salmon slap, we discontinue the event
     return false;
 }
Ejemplo n.º 11
0
 public function onSalmonSlap($endpoint_uri, MagicEnvelope $magic_env, Profile $target = null)
 {
     $envxml = $magic_env->toXML($target, 'diaspora');
     // Diaspora wants another POST format (base64url-encoded POST variable 'xml')
     $headers = array('Content-Type: application/x-www-form-urlencoded');
     // Another way to distinguish Diaspora from GNU social is that a POST with
     // $headers=array('Content-Type: application/magic-envelope+xml') would return
     // HTTP status code 422 Unprocessable Entity, at least as of 2015-10-04.
     try {
         $client = new HTTPClient();
         $client->setBody('xml=' . Magicsig::base64_url_encode($envxml));
         $response = $client->post($endpoint_uri, $headers);
     } catch (HTTP_Request2_Exception $e) {
         common_log(LOG_ERR, "Diaspora-flavoured Salmon post to {$endpoint_uri} failed: " . $e->getMessage());
         return false;
     }
     // 200 OK is the best response
     // 202 Accepted is what we get from Diaspora for example
     if (!in_array($response->getStatus(), array(200, 202))) {
         common_log(LOG_ERR, sprintf('Salmon (from profile %d) endpoint %s returned status %s: %s', $magic_env->getActor()->getID(), $endpoint_uri, $response->getStatus(), $response->getBody()));
         return true;
     }
     // Success!
     return false;
 }