Merge branch '3.2RC'
This commit is contained in:
226
include/zot.php
226
include/zot.php
@@ -158,6 +158,85 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot
|
||||
return json_encode($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Builds a zot6 notification packet.
|
||||
*
|
||||
* Builds a zot6 notification packet that you can either store in the queue with
|
||||
* a message array or call zot_zot to immediately zot it to the other side.
|
||||
*
|
||||
* @param array $channel
|
||||
* sender channel structure
|
||||
* @param string $type
|
||||
* packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check'
|
||||
* @param array $recipients
|
||||
* envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts
|
||||
* @param string $remote_key
|
||||
* optional public site key of target hub used to encrypt entire packet
|
||||
* NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others
|
||||
* @param string $methods
|
||||
* optional comma separated list of encryption methods @ref zot_best_algorithm()
|
||||
* @param string $secret
|
||||
* random string, required for packets which require verification/callback
|
||||
* e.g. 'pickup', 'purge', 'notify', 'auth_check'. Packet types 'ping', 'force_refresh', and 'refresh' do not require verification
|
||||
* @param string $extra
|
||||
* @returns string json encoded zot packet
|
||||
*/
|
||||
function zot6_build_packet($channel, $type = 'notify', $recipients = null, $msg = '', $remote_key = null, $methods = '', $secret = null, $extra = null) {
|
||||
|
||||
$sig_method = get_config('system','signature_algorithm','sha256');
|
||||
|
||||
$data = [
|
||||
'type' => $type,
|
||||
'sender' => [
|
||||
'guid' => $channel['channel_guid'],
|
||||
'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'],$sig_method)),
|
||||
'url' => z_root(),
|
||||
'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'],$sig_method)),
|
||||
'sitekey' => get_config('system','pubkey')
|
||||
],
|
||||
'callback' => '/post',
|
||||
'version' => Zotlabs\Lib\System::get_zot_revision(),
|
||||
'encryption' => crypto_methods(),
|
||||
'signing' => signing_methods()
|
||||
];
|
||||
|
||||
if ($recipients) {
|
||||
for ($x = 0; $x < count($recipients); $x ++)
|
||||
unset($recipients[$x]['hash']);
|
||||
|
||||
$data['recipients'] = $recipients;
|
||||
}
|
||||
|
||||
if($msg) {
|
||||
$data['msg'] = $msg;
|
||||
}
|
||||
|
||||
if ($secret) {
|
||||
$data['secret'] = preg_replace('/[^0-9a-fA-F]/','',$secret);
|
||||
$data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey'],$sig_method));
|
||||
}
|
||||
|
||||
if ($extra) {
|
||||
foreach ($extra as $k => $v)
|
||||
$data[$k] = $v;
|
||||
}
|
||||
|
||||
logger('zot6_build_packet: ' . print_r($data,true), LOGGER_DATA, LOG_DEBUG);
|
||||
|
||||
// Hush-hush ultra top-secret mode
|
||||
|
||||
if($remote_key) {
|
||||
$algorithm = zot_best_algorithm($methods);
|
||||
$data = crypto_encapsulate(json_encode($data),$remote_key, $algorithm);
|
||||
}
|
||||
|
||||
return json_encode($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Choose best encryption function from those available on both sites.
|
||||
*
|
||||
@@ -209,10 +288,23 @@ function zot_best_algorithm($methods) {
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $data
|
||||
* @param array $channel (optional if using zot6 delivery)
|
||||
* @param array $crypto (optional if encrypted httpsig, requires hubloc_sitekey and site_crypto elements)
|
||||
* @return array see z_post_url() for returned data format
|
||||
*/
|
||||
function zot_zot($url, $data) {
|
||||
return z_post_url($url, array('data' => $data));
|
||||
function zot_zot($url, $data, $channel = null,$crypto = null) {
|
||||
|
||||
$headers = [];
|
||||
|
||||
if($channel) {
|
||||
$headers['X-Zot-Token'] = random_string();
|
||||
$hash = \Zotlabs\Web\HTTPSig::generate_digest($data,false);
|
||||
$headers['X-Zot-Digest'] = 'SHA-256=' . $hash;
|
||||
$h = \Zotlabs\Web\HTTPSig::create_sig('',$headers,$channel['channel_prvkey'],'acct:' . $channel['channel_address'] . '@' . \App::get_hostname(),false,false,'sha512',(($crypto) ? $crypto['hubloc_sitekey'] : ''), (($crypto) ? zot_best_algorithm($crypto['site_crypto']) : ''));
|
||||
}
|
||||
|
||||
$redirects = 0;
|
||||
return z_post_url($url, array('data' => $data),$redirects,((empty($h)) ? [] : [ 'headers' => $h ]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -495,13 +587,16 @@ function zot_refresh($them, $channel = null, $force = false) {
|
||||
|
||||
|
||||
// If there is a default group for this channel, add this connection to it
|
||||
// for pending connections this will happens at acceptance time.
|
||||
|
||||
$default_group = $channel['channel_default_group'];
|
||||
if($default_group) {
|
||||
require_once('include/group.php');
|
||||
$g = group_rec_byhash($channel['channel_id'],$default_group);
|
||||
if($g)
|
||||
group_add_member($channel['channel_id'],'',$x['hash'],$g['id']);
|
||||
if(! intval($new_connection[0]['abook_pending'])) {
|
||||
$default_group = $channel['channel_default_group'];
|
||||
if($default_group) {
|
||||
require_once('include/group.php');
|
||||
$g = group_rec_byhash($channel['channel_id'],$default_group);
|
||||
if($g)
|
||||
group_add_member($channel['channel_id'],'',$x['hash'],$g['id']);
|
||||
}
|
||||
}
|
||||
|
||||
unset($new_connection[0]['abook_id']);
|
||||
@@ -1060,7 +1155,12 @@ function zot_process_response($hub, $arr, $outq) {
|
||||
* @brief
|
||||
*
|
||||
* We received a notification packet (in mod_post) that a message is waiting for us, and we've verified the sender.
|
||||
* Now send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign with our site
|
||||
* Check if the site is using zot6 delivery and includes a verified HTTP Signature, signed content, and a 'msg' field,
|
||||
* and also that the signer and the sender match.
|
||||
* If that happens, we do not need to fetch/pickup the message - we have it already and it is verified.
|
||||
* Translate it into the form we need for zot_import() and import it.
|
||||
*
|
||||
* Otherwise send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign with our site
|
||||
* private key.
|
||||
* The entire pickup message is encrypted with the remote site's public key.
|
||||
* If everything checks out on the remote end, we will receive back a packet containing one or more messages,
|
||||
@@ -1078,38 +1178,61 @@ function zot_fetch($arr) {
|
||||
|
||||
$url = $arr['sender']['url'] . $arr['callback'];
|
||||
|
||||
// set $multiple param on zot_gethub() to return all matching hubs
|
||||
// This allows us to recover from re-installs when a redundant (but invalid) hubloc for
|
||||
// this identity is widely dispersed throughout the network.
|
||||
$import = null;
|
||||
$hubs = null;
|
||||
|
||||
$ret_hubs = zot_gethub($arr['sender'],true);
|
||||
if(! $ret_hubs) {
|
||||
$zret = zot6_check_sig();
|
||||
|
||||
if($zret['success'] && $zret['hubloc'] && $zret['hubloc']['hubloc_guid'] === $data['sender']['guid'] && $data['msg']) {
|
||||
logger('zot6_delivery',LOGGER_DEBUG);
|
||||
logger('zot6_data: ' . print_r($data,true),LOGGER_DATA);
|
||||
|
||||
$ret['collected'] = true;
|
||||
|
||||
$import = [ 'success' => true, 'body' => json_encode( [ 'success' => true, 'pickup' => [ [ 'notify' => $data, 'message' => json_decode($data['msg'],true) ] ] ] ) ];
|
||||
$hubs = [ $zret['hubloc'] ] ;
|
||||
}
|
||||
|
||||
if(! $hubs) {
|
||||
// set $multiple param on zot_gethub() to return all matching hubs
|
||||
// This allows us to recover from re-installs when a redundant (but invalid) hubloc for
|
||||
// this identity is widely dispersed throughout the network.
|
||||
|
||||
$hubs = zot_gethub($arr['sender'],true);
|
||||
}
|
||||
|
||||
if(! $hubs) {
|
||||
logger('No hub: ' . print_r($arr['sender'],true));
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($ret_hubs as $ret_hub) {
|
||||
foreach($hubs as $hub) {
|
||||
|
||||
$secret = substr(preg_replace('/[^0-9a-fA-F]/','',$arr['secret']),0,64);
|
||||
if(! $import) {
|
||||
$secret = substr(preg_replace('/[^0-9a-fA-F]/','',$arr['secret']),0,64);
|
||||
|
||||
$data = [
|
||||
'type' => 'pickup',
|
||||
'url' => z_root(),
|
||||
'callback_sig' => base64url_encode(rsa_sign(z_root() . '/post', get_config('system','prvkey'))),
|
||||
'callback' => z_root() . '/post',
|
||||
'secret' => $secret,
|
||||
'secret_sig' => base64url_encode(rsa_sign($secret, get_config('system','prvkey')))
|
||||
];
|
||||
$data = [
|
||||
'type' => 'pickup',
|
||||
'url' => z_root(),
|
||||
'callback_sig' => base64url_encode(rsa_sign(z_root() . '/post', get_config('system','prvkey'))),
|
||||
'callback' => z_root() . '/post',
|
||||
'secret' => $secret,
|
||||
'secret_sig' => base64url_encode(rsa_sign($secret, get_config('system','prvkey')))
|
||||
];
|
||||
|
||||
$algorithm = zot_best_algorithm($ret_hub['site_crypto']);
|
||||
$datatosend = json_encode(crypto_encapsulate(json_encode($data),$ret_hub['hubloc_sitekey'], $algorithm));
|
||||
$algorithm = zot_best_algorithm($hub['site_crypto']);
|
||||
$datatosend = json_encode(crypto_encapsulate(json_encode($data),$hub['hubloc_sitekey'], $algorithm));
|
||||
|
||||
$fetch = zot_zot($url,$datatosend);
|
||||
$import = zot_zot($url,$datatosend);
|
||||
}
|
||||
else {
|
||||
$algorithm = zot_best_algorithm($hub['site_crypto']);
|
||||
}
|
||||
|
||||
$result = zot_import($fetch, $arr['sender']['url']);
|
||||
$result = zot_import($import, $arr['sender']['url']);
|
||||
|
||||
if($result) {
|
||||
$result = crypto_encapsulate(json_encode($result),$ret_hub['hubloc_sitekey'], $algorithm);
|
||||
$result = crypto_encapsulate(json_encode($result),$hub['hubloc_sitekey'], $algorithm);
|
||||
return $result;
|
||||
}
|
||||
|
||||
@@ -1608,7 +1731,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $
|
||||
foreach($deliveries as $d) {
|
||||
$local_public = $public;
|
||||
|
||||
$DR = new Zotlabs\Zot\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']);
|
||||
$DR = new Zotlabs\Lib\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']);
|
||||
|
||||
$r = q("select * from channel where channel_hash = '%s' limit 1",
|
||||
dbesc($d['hash'])
|
||||
@@ -2137,7 +2260,7 @@ function process_mail_delivery($sender, $arr, $deliveries) {
|
||||
|
||||
foreach($deliveries as $d) {
|
||||
|
||||
$DR = new Zotlabs\Zot\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']);
|
||||
$DR = new Zotlabs\Lib\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']);
|
||||
|
||||
$r = q("select * from channel where channel_hash = '%s' limit 1",
|
||||
dbesc($d['hash'])
|
||||
@@ -3778,11 +3901,11 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) {
|
||||
// we should probably do this for all items, but usually we only send one.
|
||||
|
||||
if(array_key_exists('item',$arr) && is_array($arr['item'][0])) {
|
||||
$DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed');
|
||||
$DR = new Zotlabs\Lib\DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed');
|
||||
$DR->addto_recipient($channel['channel_name'] . ' <' . channel_reddress($channel) . '>');
|
||||
}
|
||||
else
|
||||
$DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered');
|
||||
$DR = new Zotlabs\Lib\DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered');
|
||||
|
||||
$result[] = $DR->get();
|
||||
}
|
||||
@@ -4793,7 +4916,7 @@ function zot_reply_auth_check($data,$encrypted_packet) {
|
||||
* the web server. We should probably convert this to webserver time rather than DB time so
|
||||
* that the different clocks won't affect it and allow us to keep the time short.
|
||||
*/
|
||||
Zotlabs\Zot\Verify::purge('auth', '30 MINUTE');
|
||||
Zotlabs\Lib\Verify::purge('auth', '30 MINUTE');
|
||||
|
||||
$y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($sender_hash)
|
||||
@@ -4834,7 +4957,7 @@ function zot_reply_auth_check($data,$encrypted_packet) {
|
||||
// This additionally checks for forged sites since we already stored the expected result in meta
|
||||
// and we've already verified that this is them via zot_gethub() and that their key signed our token
|
||||
|
||||
$z = Zotlabs\Zot\Verify::match('auth',$c[0]['channel_id'],$data['secret'],$data['sender']['url']);
|
||||
$z = Zotlabs\Lib\Verify::match('auth',$c[0]['channel_id'],$data['secret'],$data['sender']['url']);
|
||||
if (! $z) {
|
||||
logger('mod_zot: auth_check: verification key not found.');
|
||||
$ret['message'] .= 'verification key not found' . EOL;
|
||||
@@ -4967,6 +5090,39 @@ function zot_reply_refresh($sender, $recipients) {
|
||||
}
|
||||
|
||||
|
||||
function zot6_check_sig() {
|
||||
|
||||
$ret = [ 'success' => false ];
|
||||
|
||||
logger('server: ' . print_r($_SERVER,true), LOGGER_DATA);
|
||||
|
||||
if(array_key_exists('HTTP_SIGNATURE',$_SERVER)) {
|
||||
$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER['HTTP_SIGNATURE']);
|
||||
if($sigblock) {
|
||||
$keyId = $sigblock['keyId'];
|
||||
|
||||
if($keyId) {
|
||||
$r = q("select hubloc.*, site_crypto from hubloc left join site on hubloc_url = site_url
|
||||
where hubloc_addr = '%s' ",
|
||||
dbesc(str_replace('acct:','',$keyId))
|
||||
);
|
||||
if($r) {
|
||||
foreach($r as $hubloc) {
|
||||
$verified = \Zotlabs\Web\HTTPSig::verify('',$hubloc['xchan_pubkey']);
|
||||
if($verified && $verified['header_signed'] && $verified['header_valid'] && $verified['content_signed'] && $verified['content_valid']) {
|
||||
$ret['hubloc'] = $hubloc;
|
||||
$ret['success'] = true;
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function zot_reply_notify($data) {
|
||||
|
||||
$ret = array('success' => false);
|
||||
|
||||
Reference in New Issue
Block a user