Merge branch '3.2RC'

This commit is contained in:
Mario
2018-03-09 11:12:18 +01:00
1817 changed files with 80348 additions and 175116 deletions

View File

@@ -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);