From 4ccd9ae6da2f4fbed45af80a11b53c8b4efd9ceb Mon Sep 17 00:00:00 2001 From: redmatrix Date: Sun, 6 Dec 2015 19:16:38 -0800 Subject: start of v4 --- Zotlabs/Identity/BasicId.php | 18 ++++++ Zotlabs/Identity/ProfilePhoto.php | 16 +++++ Zotlabs/Zot/Receiver.php | 129 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 Zotlabs/Identity/BasicId.php create mode 100644 Zotlabs/Identity/ProfilePhoto.php create mode 100644 Zotlabs/Zot/Receiver.php (limited to 'Zotlabs') diff --git a/Zotlabs/Identity/BasicId.php b/Zotlabs/Identity/BasicId.php new file mode 100644 index 000000000..3c149808f --- /dev/null +++ b/Zotlabs/Identity/BasicId.php @@ -0,0 +1,18 @@ +error = false; + $this->validated = false; + $this->messagetype = ''; + $this->response = array('success' => false); + + if(! is_array($data)) + $data = json_decode($data,true); + + if($data && is_array($data)) { + $this->encrypted = ((array_key_exists('iv',$data)) ? true : false); + + if($this->encrypted) { + $this->data = @json_decode(@crypto_unencapsulate($data,$prvkey),true); + } + if(! $this->data) + $this->data = $data; + + if($this->data && is_array($this->data) && array_key_exists('type',$this->data)) + $this->messagetype = $this->data['type']; + } + if(! $this->messagetype) + $error = true; + + $this->sender = ((array_key_exists('sender',$this->data)) ? $this->data['sender'] : null); + $this->recipients = ((array_key_exists('recipients',$this->data)) ? $this->data['recipients'] : null); + + + if($this->sender) + $this->ValidateSender(); + + $this->Dispatch(); + + + } + + function ValidateSender() { + $hubs = zot_gethub($this->sender,true); + if (! $hubs) { + + /* Have never seen this guid or this guid coming from this location. Check it and register it. */ + /* (!!) this will validate the sender. */ + + $result = zot_register_hub($this->sender); + + if ((! $result['success']) || (! ($hubs = zot_gethub($this->sender,true)))) { + $this->response['message'] = 'Hub not available.'; + json_return_and_die($this->response); + } + } + foreach($hubs as $hub) { + update_hub_connected($hub,((array_key_exists('sitekey',$this->sender)) ? $this->sender['sitekey'] : '')); + } + $this->validated = true; + } + + + function Dispatch() { + + /* Handle tasks which don't require sender validation */ + + switch($this->messagetype) { + case 'ping': + /* no validation needed */ + zot_reply_ping(); + break; + case 'pickup': + zot_reply_pickup($this->data); + /* perform site validation, as opposed to sender validation */ + break; + + default: + if(! $this->validated) { + $this->response['message'] = 'Sender not valid'; + json_return_and_die($this->response); + } + break; + } + + /* Now handle tasks which require sender validation */ + + switch($this->messagetype) { + + case 'auth_check': + zot_reply_auth_check($this->data,$this->encrypted); + break; + + case 'request': + json_return_and_die(zot_process_message_request($this->data)); + break; + + case 'purge': + zot_reply_purge($this->sender,$this->recipients); + break; + + case 'refresh': + case 'force_refresh': + zot_reply_refresh($this->sender,$this->recipients); + break; + + case 'notify': + zot_reply_notify($this->data); + break; + + default: + $this->response['message'] = 'Not implemented'; + json_return_and_die($this->response); + break; + } + + } +} -- cgit v1.2.3 From 4d301bc5a698d8a3f8081d3b7acec85546b7d0bd Mon Sep 17 00:00:00 2001 From: redmatrix Date: Sun, 6 Dec 2015 19:29:01 -0800 Subject: comment order --- Zotlabs/Zot/Receiver.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Receiver.php b/Zotlabs/Zot/Receiver.php index b74efe804..493d6fd08 100644 --- a/Zotlabs/Zot/Receiver.php +++ b/Zotlabs/Zot/Receiver.php @@ -2,7 +2,6 @@ namespace Zotlabs\Zot; - class Receiver { protected $data; @@ -82,8 +81,8 @@ class Receiver { zot_reply_ping(); break; case 'pickup': - zot_reply_pickup($this->data); /* perform site validation, as opposed to sender validation */ + zot_reply_pickup($this->data); break; default: -- cgit v1.2.3 From 1c22e8ae7ed8bc08bbc71eefa26a6d0aabcd5e7d Mon Sep 17 00:00:00 2001 From: redmatrix Date: Sun, 6 Dec 2015 19:32:39 -0800 Subject: whitespace --- Zotlabs/Zot/Receiver.php | 2 -- 1 file changed, 2 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Receiver.php b/Zotlabs/Zot/Receiver.php index 493d6fd08..21573dd7f 100644 --- a/Zotlabs/Zot/Receiver.php +++ b/Zotlabs/Zot/Receiver.php @@ -46,8 +46,6 @@ class Receiver { $this->ValidateSender(); $this->Dispatch(); - - } function ValidateSender() { -- cgit v1.2.3 From 2d799f2c114bbd540a6b2f260e512ad5fcf1db5c Mon Sep 17 00:00:00 2001 From: redmatrix Date: Sun, 6 Dec 2015 19:59:46 -0800 Subject: remove duplicated code --- Zotlabs/Zot/Receiver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Receiver.php b/Zotlabs/Zot/Receiver.php index 21573dd7f..b4d5d5ef6 100644 --- a/Zotlabs/Zot/Receiver.php +++ b/Zotlabs/Zot/Receiver.php @@ -36,7 +36,7 @@ class Receiver { $this->messagetype = $this->data['type']; } if(! $this->messagetype) - $error = true; + $this->error = true; $this->sender = ((array_key_exists('sender',$this->data)) ? $this->data['sender'] : null); $this->recipients = ((array_key_exists('recipients',$this->data)) ? $this->data['recipients'] : null); -- cgit v1.2.3 From 8b974f8f74f6eefc5955a0f9e385f6460504fc42 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Mon, 7 Dec 2015 16:01:54 -0800 Subject: abstract the message handlers --- Zotlabs/Zot/IHandler.php | 22 ++++++++++++++++++++++ Zotlabs/Zot/Receiver.php | 19 +++++++++++-------- Zotlabs/Zot/ZotHandler.php | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 Zotlabs/Zot/IHandler.php create mode 100644 Zotlabs/Zot/ZotHandler.php (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/IHandler.php b/Zotlabs/Zot/IHandler.php new file mode 100644 index 000000000..eeca1555c --- /dev/null +++ b/Zotlabs/Zot/IHandler.php @@ -0,0 +1,22 @@ +error = false; $this->validated = false; $this->messagetype = ''; $this->response = array('success' => false); + $this->handler = $handler; + if(! is_array($data)) $data = json_decode($data,true); @@ -76,11 +79,11 @@ class Receiver { switch($this->messagetype) { case 'ping': /* no validation needed */ - zot_reply_ping(); + $this->handler->Ping(); break; case 'pickup': /* perform site validation, as opposed to sender validation */ - zot_reply_pickup($this->data); + $this->handler->Pickup($this->data); break; default: @@ -96,24 +99,24 @@ class Receiver { switch($this->messagetype) { case 'auth_check': - zot_reply_auth_check($this->data,$this->encrypted); + $this->handler->AuthCheck($this->data,$this->encrypted); break; case 'request': - json_return_and_die(zot_process_message_request($this->data)); + $this->handler->Request($this->data); break; case 'purge': - zot_reply_purge($this->sender,$this->recipients); + $this->handler->Purge($this->sender,$this->recipients); break; case 'refresh': case 'force_refresh': - zot_reply_refresh($this->sender,$this->recipients); + $this->handler->Refresh($this->sender,$this->recipients); break; case 'notify': - zot_reply_notify($this->data); + $this->handler->Notify($this->data); break; default: diff --git a/Zotlabs/Zot/ZotHandler.php b/Zotlabs/Zot/ZotHandler.php new file mode 100644 index 000000000..20332074e --- /dev/null +++ b/Zotlabs/Zot/ZotHandler.php @@ -0,0 +1,38 @@ + Date: Mon, 7 Dec 2015 16:06:43 -0800 Subject: extra paren removed --- Zotlabs/Zot/ZotHandler.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/ZotHandler.php b/Zotlabs/Zot/ZotHandler.php index 20332074e..f9bb05410 100644 --- a/Zotlabs/Zot/ZotHandler.php +++ b/Zotlabs/Zot/ZotHandler.php @@ -20,7 +20,7 @@ class ZotHandler implements IHandler { } function Request($data) { - zot_reply_message_request($data)); + zot_reply_message_request($data); } function AuthCheck($data,$encrypted) { -- cgit v1.2.3 From baab9b6fbc45a30dc4cce5b3c85d7c94e8e6a84c Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 8 Dec 2015 18:31:33 -0800 Subject: more libzot stuff --- Zotlabs/Zot/Auth.php | 356 +++++++++++++++++++++++++++++++++++++++++++++++ Zotlabs/Zot/Receiver.php | 167 ++++++++++++++++++++++ 2 files changed, 523 insertions(+) create mode 100644 Zotlabs/Zot/Auth.php (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php new file mode 100644 index 000000000..950a7593a --- /dev/null +++ b/Zotlabs/Zot/Auth.php @@ -0,0 +1,356 @@ +ret = array('success' => false); + $this->success = false; + $this->test = ((array_key_exists('test',$req)) ? intval($req['test']) : 0); + $this->address = $req['auth']; + $this->desturl = $req['dest']; + $this->sec = $req['sec']; + $this->version = $req['version']; + $this->delegate = $req['delegate']; + + $c = get_sys_channel(); + if(! $c) { + logger('unable to obtain response (sys) channel'); + reply_die('no local channels found.'); + } + + $x = $this->GetHublocs($this->address); + + logger('hublocs'); + + foreach($x as $xx) { + + logger('verify'); + + if($this->Verify($c,$xx)) + break; + } + + /** + * @FIXME we really want to save the return_url in the session before we + * visit rmagic. This does however prevent a recursion if you visit + * rmagic directly, as it would otherwise send you back here again. + * But z_root() probably isn't where you really want to go. + */ + + if(strstr($this->desturl,z_root() . '/rmagic')) + goaway(z_root()); + + $this->reply_die(); + + } + + function GetHublocs($address) { + + // Try and find a hubloc for the person attempting to auth + $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + where hubloc_addr = '%s' order by hubloc_id desc", + dbesc($address) + ); + + if(! $x) { + // finger them if they can't be found. + $ret = zot_finger($address, null); + if ($ret['success']) { + $j = json_decode($ret['body'], true); + if ($j) + import_xchan($j); + $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + where hubloc_addr = '%s' order by hubloc_id desc", + dbesc($address) + ); + } + } + if(! $x) { + logger('mod_zot: auth: unable to finger ' . $address); + $this->reply_die('no hubloc found for ' . $address . ' and probing failed.'); + } + + return $x; + } + + + function Verify($channel,$hubloc) { + logger('auth request received from ' . $hubloc['hubloc_addr'] ); + + // check credentials and access + + // If they are already authenticated and haven't changed credentials, + // we can save an expensive network round trip and improve performance. + + $this->remote = remote_channel(); + $this->remote_service_class = ''; + $this->remote_level = 0; + $this->remote_hub = $hubloc['hubloc_url']; + $this->dnt = 0; + + // Also check that they are coming from the same site as they authenticated with originally. + + $already_authed = ((($this->remote) && ($hubloc['hubloc_hash'] == $this->remote) + && ($hubloc['hubloc_url'] === $_SESSION['remote_hub'])) ? true : false); + if($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) + $already_authed = false; + + $j = array(); + + if(! $already_authed) { + + // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the + // site private key + // The actual channel sending the packet ($c[0]) is not important, but this provides a + // generic zot packet with a sender which can be verified + + $p = zot_build_packet($channel,$type = 'auth_check', + array(array('guid' => $hubloc['hubloc_guid'],'guid_sig' => $hubloc['hubloc_guid_sig'])), + $hubloc['hubloc_sitekey'], $this->sec); + + $this->dbg_msg('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); + $this->dbg_msg('packet contents: ' . $p); + + + $result = zot_zot($hubloc['hubloc_callback'],$p); + + if(! $result['success']) { + logger('auth_check callback failed.'); + if($this->test) { + $this->dbg_msg('auth check request to your site returned .' . print_r($result, true)); + return false; + } + return false; + } + $j = json_decode($result['body'], true); + if(! $j) { + logger('auth_check json data malformed.'); + if($this->test) { + $this->dbg_msg('json malformed: ' . $result['body']); + return false; + } + } + + $this->dbg_msg('auth check request returned .' . print_r($j, true)); + + if ($already_authed || $j['success']) { + if($j['success']) { + // legit response, but we do need to check that this wasn't answered by a man-in-middle + if (! rsa_verify($this->sec . $hubloc['xchan_hash'],base64url_decode($j['confirm']),$hubloc['xchan_pubkey'])) { + logger('final confirmation failed.'); + if($this->test) { + $this->dbg_msg('final confirmation failed. ' . $sec . print_r($j,true) . print_r($hubloc,true)); + return false; + } + return false; + } + if (array_key_exists('service_class',$j)) + $this->remote_service_class = $j['service_class']; + if (array_key_exists('level',$j)) + $this->remote_level = $j['level']; + if (array_key_exists('DNT',$j)) + $this->dnt = $j['DNT']; + } + + // everything is good... maybe + + if(local_channel()) { + + // tell them to logout if they're logged in locally as anything but the target remote account + // in which case just shut up because they don't need to be doing this at all. + + if (get_app()->channel['channel_hash'] != $hubloc['xchan_hash']) { + logger('already authenticated locally as somebody else.'); + notice( t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); + if($this->test) { + $$this->dbg_msg('already logged in locally with a conflicting identity.'); + return false;; + } + } + return false;; + } + + // log them in + + if ($this->test) { + $ret['success'] = true; + $this->reply_die('Authentication Success!'); + } + + $this->delegate_success = false; + if($this->delegate) { + $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", + dbesc($this->delegate) + ); + if ($r && intval($r[0]['channel_id'])) { + $allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate'); + if($allowed) { + $_SESSION['delegate_channel'] = $r[0]['channel_id']; + $_SESSION['delegate'] = $hubloc['xchan_hash']; + $_SESSION['account_id'] = intval($r[0]['channel_account_id']); + require_once('include/security.php'); + change_channel($r[0]['channel_id']); + $this->delegate_success = true; + } + } + } + + $_SESSION['authenticated'] = 1; + if (! $this->delegate_success) { + $_SESSION['visitor_id'] = $hubloc['xchan_hash']; + $_SESSION['my_url'] = $hubloc['xchan_url']; + $_SESSION['my_address'] = $this->address; + $_SESSION['remote_service_class'] = $this->remote_service_class; + $_SESSION['remote_level'] = $this->remote_level; + $_SESSION['remote_hub'] = $this->remote_hub; + $_SESSION['DNT'] = $this->dnt; + } + + $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); + call_hooks('magic_auth_success',$arr); + get_app()->set_observer($hubloc); + require_once('include/security.php'); + get_app()->set_groups(init_groups_visitor($_SESSION['visitor_id'])); + info(sprintf( t('Welcome %s. Remote authentication successful.'),$hubloc['xchan_name'])); + logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); + $this->success = true; + return true; + } + else { + if($this->test) { + $this->dbg_msg('auth failure. ' . print_r($_REQUEST,true) . print_r($j,true)); + return false; + } + logger('magic-auth failure - not authenticated: ' . $hubloc['xchan_addr']); + } + + if($this->test) { + $this->dbg_msg('auth failure fallthrough ' . print_r($_REQUEST,true) . print_r($j,true)); + return false; + } + } + } + + + function dbg_msg($msg) { + if($msg) { + if(array_key_exists('message',$this->ret)) + $this->ret['message'] .= $msg; + else + $this->ret['message'] = $msg; + } + } + + + function reply_die($msg,$goaway = true) { + if($msg) { + if(array_key_exists('message',$this->ret)) + $this->ret['message'] .= $msg; + else + $this->ret['message'] = $msg; + } + if($this->test) + json_return_and_die($this->ret); + if($goaway) + goaway($this->desturl); + } + +} + + +/** + * @brief HTTP POST entry point for Zot. + * + * Most access to this endpoint is via the post method. + * Here we will pick out the magic auth params which arrive as a get request, + * and the only communications to arrive this way. + * + * Magic Auth + * ========== + * + * So-called "magic auth" takes place by a special exchange. On the site where the "channel to be authenticated" lives (e.g. $mysite), + * a redirection is made via $mysite/magic to the zot endpoint of the remote site ($remotesite) with special GET parameters. + * + * The endpoint is typically https://$remotesite/post - or whatever was specified as the callback url in prior communications + * (we will bootstrap an address and fetch a zot info packet if possible where no prior communications exist) + * + * Five GET parameters are supplied: + * * auth => the urlencoded webbie (channel@host.domain) of the channel requesting access + * * dest => the desired destination URL (urlencoded) + * * sec => a random string which is also stored on $mysite for use during the verification phase. + * * version => the zot revision + * * delegate => optional urlencoded webbie of a local channel to invoke delegation rights for + * + * When this packet is received, an "auth-check" zot message is sent to $mysite. + * (e.g. if $_GET['auth'] is foobar@podunk.edu, a zot packet is sent to the podunk.edu zot endpoint, which is typically /post) + * If no information has been recorded about the requesting identity a zot information packet will be retrieved before + * continuing. + * + * The sender of this packet is an arbitrary/random site channel. The recipients will be a single recipient corresponding + * to the guid and guid_sig we have associated with the requesting auth identity + * + * \code{.json} + * { + * "type":"auth_check", + * "sender":{ + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TApz...", + * "url":"http:\/\/podunk.edu", + * "url_sig":"T8Bp7j..." + * }, + * "recipients":{ + * { + * "guid":"ZHSqb...", + * "guid_sig":"JsAAXi..." + * } + * } + * "callback":"\/post", + * "version":1, + * "secret":"1eaa661", + * "secret_sig":"eKV968b1..." + * } + * \endcode + * + * auth_check messages MUST use encapsulated encryption. This message is sent to the origination site, which checks the 'secret' to see + * if it is the same as the 'sec' which it passed originally. It also checks the secret_sig which is the secret signed by the + * destination channel's private key and base64url encoded. If everything checks out, a json packet is returned: + * + * \code{.json} + * { + * "success":1, + * "confirm":"q0Ysovd1u...", + * "service_class":(optional) + * "level":(optional) + * } + * \endcode + * + * 'confirm' in this case is the base64url encoded RSA signature of the concatenation of 'secret' with the + * base64url encoded whirlpool hash of the requestor's guid and guid_sig; signed with the source channel private key. + * This prevents a man-in-the-middle from inserting a rogue success packet. Upon receipt and successful + * verification of this packet, the destination site will redirect to the original destination URL and indicate a successful remote login. + * Service_class can be used by cooperating sites to provide different access rights based on account rights and subscription plans. It is + * a string whose contents are not defined by protocol. Example: "basic" or "gold". + * + * @param[in,out] App &$a + */ diff --git a/Zotlabs/Zot/Receiver.php b/Zotlabs/Zot/Receiver.php index 6a11bcde0..238de1332 100644 --- a/Zotlabs/Zot/Receiver.php +++ b/Zotlabs/Zot/Receiver.php @@ -127,3 +127,170 @@ class Receiver { } } + + + +/** + * @brief zot communications and messaging. + * + * Sender HTTP posts to this endpoint ($site/post typically) with 'data' parameter set to json zot message packet. + * This packet is optionally encrypted, which we will discover if the json has an 'iv' element. + * $contents => array( 'alg' => 'aes256cbc', 'iv' => initialisation vector, 'key' => decryption key, 'data' => encrypted data); + * $contents->iv and $contents->key are random strings encrypted with this site's RSA public key and then base64url encoded. + * Currently only 'aes256cbc' is used, but this is extensible should that algorithm prove inadequate. + * + * Once decrypted, one will find the normal json_encoded zot message packet. + * + * Defined packet types are: notify, purge, refresh, force_refresh, auth_check, ping, and pickup + * + * Standard packet: (used by notify, purge, refresh, force_refresh, and auth_check) + * \code{.json} + * { + * "type": "notify", + * "sender":{ + * "guid":"kgVFf_1...", + * "guid_sig":"PT9-TApzp...", + * "url":"http:\/\/podunk.edu", + * "url_sig":"T8Bp7j5...", + * }, + * "recipients": { optional recipient array }, + * "callback":"\/post", + * "version":1, + * "secret":"1eaa...", + * "secret_sig": "df89025470fac8..." + * } + * \endcode + * + * Signature fields are all signed with the sender channel private key and base64url encoded. + * Recipients are arrays of guid and guid_sig, which were previously signed with the recipients private + * key and base64url encoded and later obtained via channel discovery. Absence of recipients indicates + * a public message or visible to all potential listeners on this site. + * + * "pickup" packet: + * The pickup packet is sent in response to a notify packet from another site + * \code{.json} + * { + * "type":"pickup", + * "url":"http:\/\/example.com", + * "callback":"http:\/\/example.com\/post", + * "callback_sig":"teE1_fLI...", + * "secret":"1eaa...", + * "secret_sig":"O7nB4_..." + * } + * \endcode + * + * In the pickup packet, the sig fields correspond to the respective data + * element signed with this site's system private key and then base64url encoded. + * The "secret" is the same as the original secret from the notify packet. + * + * If verification is successful, a json structure is returned containing a + * success indicator and an array of type 'pickup'. + * Each pickup element contains the original notify request and a message field + * whose contents are dependent on the message type. + * + * This JSON array is AES encapsulated using the site public key of the site + * that sent the initial zot pickup packet. + * Using the above example, this would be example.com. + * + * \code{.json} + * { + * "success":1, + * "pickup":{ + * "notify":{ + * "type":"notify", + * "sender":{ + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TApz...", + * "url":"http:\/\/z.podunk.edu", + * "url_sig":"T8Bp7j5D..." + * }, + * "callback":"\/post", + * "version":1, + * "secret":"1eaa661..." + * }, + * "message":{ + * "type":"activity", + * "message_id":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", + * "message_top":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", + * "message_parent":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", + * "created":"2012-11-20 04:04:16", + * "edited":"2012-11-20 04:04:16", + * "title":"", + * "body":"Hi Nickordo", + * "app":"", + * "verb":"post", + * "object_type":"", + * "target_type":"", + * "permalink":"", + * "location":"", + * "longlat":"", + * "owner":{ + * "name":"Indigo", + * "address":"indigo@podunk.edu", + * "url":"http:\/\/podunk.edu", + * "photo":{ + * "mimetype":"image\/jpeg", + * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" + * }, + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TAp...", + * }, + * "author":{ + * "name":"Indigo", + * "address":"indigo@podunk.edu", + * "url":"http:\/\/podunk.edu", + * "photo":{ + * "mimetype":"image\/jpeg", + * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" + * }, + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TAp..." + * } + * } + * } + * } + * \endcode + * + * Currently defined message types are 'activity', 'mail', 'profile', 'location' + * and 'channel_sync', which each have different content schemas. + * + * Ping packet: + * A ping packet does not require any parameters except the type. It may or may + * not be encrypted. + * + * \code{.json} + * { + * "type": "ping" + * } + * \endcode + * + * On receipt of a ping packet a ping response will be returned: + * + * \code{.json} + * { + * "success" : 1, + * "site" { + * "url": "http:\/\/podunk.edu", + * "url_sig": "T8Bp7j5...", + * "sitekey": "-----BEGIN PUBLIC KEY----- + * MIICIjANBgkqhkiG9w0BAQE..." + * } + * } + * \endcode + * + * The ping packet can be used to verify that a site has not been re-installed, and to + * initiate corrective action if it has. The url_sig is signed with the site private key + * and base64url encoded - and this should verify with the enclosed sitekey. Failure to + * verify indicates the site is corrupt or otherwise unable to communicate using zot. + * This return packet is not otherwise verified, so should be compared with other + * results obtained from this site which were verified prior to taking action. For instance + * if you have one verified result with this signature and key, and other records for this + * url which have different signatures and keys, it indicates that the site was re-installed + * and corrective action may commence (remove or mark invalid any entries with different + * signatures). + * If you have no records which match this url_sig and key - no corrective action should + * be taken as this packet may have been returned by an imposter. + * + * @param[in,out] App &$a + */ + -- cgit v1.2.3 From 5735cad45795d1f810085a079106f9fea056bcca Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 8 Dec 2015 19:43:49 -0800 Subject: remove extra debug logging and ensure we don't try to authenticate without a hubloc --- Zotlabs/Zot/Auth.php | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php index 950a7593a..d0374ad7e 100644 --- a/Zotlabs/Zot/Auth.php +++ b/Zotlabs/Zot/Auth.php @@ -22,7 +22,6 @@ class Auth { function __construct($req) { - logger('construct'); $this->ret = array('success' => false); $this->success = false; @@ -41,14 +40,11 @@ class Auth { $x = $this->GetHublocs($this->address); - logger('hublocs'); - - foreach($x as $xx) { - - logger('verify'); - - if($this->Verify($c,$xx)) - break; + if($x) { + foreach($x as $xx) { + if($this->Verify($c,$xx)) + break; + } } /** -- cgit v1.2.3 From 200eabe0523ec89085c7546bed1c624bd66dcd24 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Wed, 9 Dec 2015 11:51:31 -0800 Subject: add empty arg --- Zotlabs/Zot/Auth.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php index d0374ad7e..715676979 100644 --- a/Zotlabs/Zot/Auth.php +++ b/Zotlabs/Zot/Auth.php @@ -57,7 +57,7 @@ class Auth { if(strstr($this->desturl,z_root() . '/rmagic')) goaway(z_root()); - $this->reply_die(); + $this->reply_die(''); } @@ -259,7 +259,7 @@ class Auth { } - function reply_die($msg,$goaway = true) { + function reply_die($msg) { if($msg) { if(array_key_exists('message',$this->ret)) $this->ret['message'] .= $msg; @@ -268,8 +268,8 @@ class Auth { } if($this->test) json_return_and_die($this->ret); - if($goaway) - goaway($this->desturl); + + goaway($this->desturl); } } -- cgit v1.2.3 From bd37f59829bbb72b66dd4a9edb16c4506e77eaed Mon Sep 17 00:00:00 2001 From: redmatrix Date: Wed, 9 Dec 2015 20:51:00 -0800 Subject: simplify magic-auth - a lot... Would be even simpler if we didn't need to provide remote debugging, which takes up about half the code. But we need that because nobody wants to try and debug this shit by asking somebody at the other end to report what's in their logfile. We've tried this repeatedly. The only thing we can do is bring back all the debugging data so you can look at it yourself. --- Zotlabs/Zot/Auth.php | 298 +++++++++++++++++++++++++-------------------------- 1 file changed, 147 insertions(+), 151 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php index 715676979..f9a1de8ab 100644 --- a/Zotlabs/Zot/Auth.php +++ b/Zotlabs/Zot/Auth.php @@ -4,8 +4,10 @@ namespace Zotlabs\Zot; class Auth { - protected $ret; protected $test; + protected $test_results; + protected $debug_msg; + protected $address; protected $desturl; protected $sec; @@ -23,19 +25,22 @@ class Auth { function __construct($req) { - $this->ret = array('success' => false); - $this->success = false; - $this->test = ((array_key_exists('test',$req)) ? intval($req['test']) : 0); - $this->address = $req['auth']; - $this->desturl = $req['dest']; - $this->sec = $req['sec']; - $this->version = $req['version']; - $this->delegate = $req['delegate']; + $this->test = ((array_key_exists('test',$req)) ? intval($req['test']) : 0); + $this->test_results = array('success' => false); + $this->debug_msg = ''; + + $this->success = false; + $this->address = $req['auth']; + $this->desturl = $req['dest']; + $this->sec = $req['sec']; + $this->version = $req['version']; + $this->delegate = $req['delegate']; $c = get_sys_channel(); if(! $c) { logger('unable to obtain response (sys) channel'); - reply_die('no local channels found.'); + $this->Debug('no local channels found.'); + $this->Finalise(); } $x = $this->GetHublocs($this->address); @@ -57,13 +62,17 @@ class Auth { if(strstr($this->desturl,z_root() . '/rmagic')) goaway(z_root()); - $this->reply_die(''); + $this->Finalise(); } function GetHublocs($address) { - // Try and find a hubloc for the person attempting to auth + // Try and find a hubloc for the person attempting to auth. + // Since we're matching by address, we have to return all entries + // some of which may be from re-installed hubs; and we'll need to + // try each sequentially to see if one can pass the test + $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' order by hubloc_id desc", dbesc($address) @@ -74,7 +83,7 @@ class Auth { $ret = zot_finger($address, null); if ($ret['success']) { $j = json_decode($ret['body'], true); - if ($j) + if($j) import_xchan($j); $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' order by hubloc_id desc", @@ -84,7 +93,8 @@ class Auth { } if(! $x) { logger('mod_zot: auth: unable to finger ' . $address); - $this->reply_die('no hubloc found for ' . $address . ' and probing failed.'); + $this->Debug('no hubloc found for ' . $address . ' and probing failed.'); + $this->Finalise(); } return $x; @@ -107,167 +117,154 @@ class Auth { // Also check that they are coming from the same site as they authenticated with originally. - $already_authed = ((($this->remote) && ($hubloc['hubloc_hash'] == $this->remote) + $already_authed = (((remote_channel()) && ($hubloc['hubloc_hash'] == remote_channel()) && ($hubloc['hubloc_url'] === $_SESSION['remote_hub'])) ? true : false); - if($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) - $already_authed = false; - - $j = array(); - - if(! $already_authed) { + + if($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) + $already_authed = false; - // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the - // site private key - // The actual channel sending the packet ($c[0]) is not important, but this provides a - // generic zot packet with a sender which can be verified + if($already_authed) + return true; - $p = zot_build_packet($channel,$type = 'auth_check', - array(array('guid' => $hubloc['hubloc_guid'],'guid_sig' => $hubloc['hubloc_guid_sig'])), - $hubloc['hubloc_sitekey'], $this->sec); + if(local_channel()) { - $this->dbg_msg('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); - $this->dbg_msg('packet contents: ' . $p); + // tell them to logout if they're logged in locally as anything but the target remote account + // in which case just shut up because they don't need to be doing this at all. - - $result = zot_zot($hubloc['hubloc_callback'],$p); - - if(! $result['success']) { - logger('auth_check callback failed.'); - if($this->test) { - $this->dbg_msg('auth check request to your site returned .' . print_r($result, true)); - return false; - } - return false; + if (get_app()->channel['channel_hash'] == $hubloc['xchan_hash']) { + return true; } - $j = json_decode($result['body'], true); - if(! $j) { - logger('auth_check json data malformed.'); + else { + logger('already authenticated locally as somebody else.'); + notice( t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); if($this->test) { - $this->dbg_msg('json malformed: ' . $result['body']); + $this->Debug('already logged in locally with a conflicting identity.'); return false; } } + return false; + } - $this->dbg_msg('auth check request returned .' . print_r($j, true)); - - if ($already_authed || $j['success']) { - if($j['success']) { - // legit response, but we do need to check that this wasn't answered by a man-in-middle - if (! rsa_verify($this->sec . $hubloc['xchan_hash'],base64url_decode($j['confirm']),$hubloc['xchan_pubkey'])) { - logger('final confirmation failed.'); - if($this->test) { - $this->dbg_msg('final confirmation failed. ' . $sec . print_r($j,true) . print_r($hubloc,true)); - return false; - } - return false; - } - if (array_key_exists('service_class',$j)) - $this->remote_service_class = $j['service_class']; - if (array_key_exists('level',$j)) - $this->remote_level = $j['level']; - if (array_key_exists('DNT',$j)) - $this->dnt = $j['DNT']; - } + // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the + // site private key + // The actual channel sending the packet ($c[0]) is not important, but this provides a + // generic zot packet with a sender which can be verified - // everything is good... maybe + $p = zot_build_packet($channel,$type = 'auth_check', + array(array('guid' => $hubloc['hubloc_guid'],'guid_sig' => $hubloc['hubloc_guid_sig'])), + $hubloc['hubloc_sitekey'], $this->sec); - if(local_channel()) { + $this->Debug('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); + $this->Debug('packet contents: ' . $p); - // tell them to logout if they're logged in locally as anything but the target remote account - // in which case just shut up because they don't need to be doing this at all. + $result = zot_zot($hubloc['hubloc_callback'],$p); - if (get_app()->channel['channel_hash'] != $hubloc['xchan_hash']) { - logger('already authenticated locally as somebody else.'); - notice( t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); - if($this->test) { - $$this->dbg_msg('already logged in locally with a conflicting identity.'); - return false;; - } - } - return false;; - } + if(! $result['success']) { + logger('auth_check callback failed.'); + if($this->test) { + $this->Debug('auth check request to your site returned .' . print_r($result, true)); + return false; + } + return false; + } + $j = json_decode($result['body'], true); + if(! $j) { + logger('auth_check json data malformed.'); + if($this->test) { + $this->Debug('json malformed: ' . $result['body']); + return false; + } + } - // log them in + $this->Debug('auth check request returned .' . print_r($j, true)); - if ($this->test) { - $ret['success'] = true; - $this->reply_die('Authentication Success!'); - } + if(! $j['success']) + return false; - $this->delegate_success = false; - if($this->delegate) { - $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", - dbesc($this->delegate) - ); - if ($r && intval($r[0]['channel_id'])) { - $allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate'); - if($allowed) { - $_SESSION['delegate_channel'] = $r[0]['channel_id']; - $_SESSION['delegate'] = $hubloc['xchan_hash']; - $_SESSION['account_id'] = intval($r[0]['channel_account_id']); - require_once('include/security.php'); - change_channel($r[0]['channel_id']); - $this->delegate_success = true; - } - } - } + // legit response, but we do need to check that this wasn't answered by a man-in-middle - $_SESSION['authenticated'] = 1; - if (! $this->delegate_success) { - $_SESSION['visitor_id'] = $hubloc['xchan_hash']; - $_SESSION['my_url'] = $hubloc['xchan_url']; - $_SESSION['my_address'] = $this->address; - $_SESSION['remote_service_class'] = $this->remote_service_class; - $_SESSION['remote_level'] = $this->remote_level; - $_SESSION['remote_hub'] = $this->remote_hub; - $_SESSION['DNT'] = $this->dnt; - } - - $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); - call_hooks('magic_auth_success',$arr); - get_app()->set_observer($hubloc); - require_once('include/security.php'); - get_app()->set_groups(init_groups_visitor($_SESSION['visitor_id'])); - info(sprintf( t('Welcome %s. Remote authentication successful.'),$hubloc['xchan_name'])); - logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); - $this->success = true; - return true; + if (! rsa_verify($this->sec . $hubloc['xchan_hash'],base64url_decode($j['confirm']),$hubloc['xchan_pubkey'])) { + logger('final confirmation failed.'); + if($this->test) { + $this->Debug('final confirmation failed. ' . $sec . print_r($j,true) . print_r($hubloc,true)); + return false; } - else { - if($this->test) { - $this->dbg_msg('auth failure. ' . print_r($_REQUEST,true) . print_r($j,true)); - return false; + return false; + } + + if (array_key_exists('service_class',$j)) + $this->remote_service_class = $j['service_class']; + if (array_key_exists('level',$j)) + $this->remote_level = $j['level']; + if (array_key_exists('DNT',$j)) + $this->dnt = $j['DNT']; + + + // log them in + + if ($this->test) { + // testing only - return the success result + $this->test_results['success'] = true; + $this->Debug('Authentication Success!'); + $this->Finalise(); + } + + $_SESSION['authenticated'] = 1; + + // check for delegation + $this->delegate_success = false; + + if($this->delegate) { + $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", + dbesc($this->delegate) + ); + if ($r && intval($r[0]['channel_id'])) { + $allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate'); + if($allowed) { + $_SESSION['delegate_channel'] = $r[0]['channel_id']; + $_SESSION['delegate'] = $hubloc['xchan_hash']; + $_SESSION['account_id'] = intval($r[0]['channel_account_id']); + require_once('include/security.php'); + // this will set the local_channel authentication in the session + change_channel($r[0]['channel_id']); + $this->delegate_success = true; } - logger('magic-auth failure - not authenticated: ' . $hubloc['xchan_addr']); } + } - if($this->test) { - $this->dbg_msg('auth failure fallthrough ' . print_r($_REQUEST,true) . print_r($j,true)); - return false; - } + if (! $this->delegate_success) { + // normal visitor (remote_channel) login session credentials + $_SESSION['visitor_id'] = $hubloc['xchan_hash']; + $_SESSION['my_url'] = $hubloc['xchan_url']; + $_SESSION['my_address'] = $this->address; + $_SESSION['remote_service_class'] = $this->remote_service_class; + $_SESSION['remote_level'] = $this->remote_level; + $_SESSION['remote_hub'] = $this->remote_hub; + $_SESSION['DNT'] = $this->dnt; } - } + $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); + call_hooks('magic_auth_success',$arr); + get_app()->set_observer($hubloc); + require_once('include/security.php'); + get_app()->set_groups(init_groups_visitor($_SESSION['visitor_id'])); + info(sprintf( t('Welcome %s. Remote authentication successful.'),$hubloc['xchan_name'])); + logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); + $this->success = true; + return true; + } - function dbg_msg($msg) { - if($msg) { - if(array_key_exists('message',$this->ret)) - $this->ret['message'] .= $msg; - else - $this->ret['message'] = $msg; - } + function Debug($msg) { + $this->debug_msg .= $msg . EOL; } - function reply_die($msg) { - if($msg) { - if(array_key_exists('message',$this->ret)) - $this->ret['message'] .= $msg; - else - $this->ret['message'] = $msg; + function Finalise() { + + if($this->test) { + $this->test_results['message'] = $this->debug_msg; + json_return_and_die($this->test_results); } - if($this->test) - json_return_and_die($this->ret); goaway($this->desturl); } @@ -276,11 +273,6 @@ class Auth { /** - * @brief HTTP POST entry point for Zot. - * - * Most access to this endpoint is via the post method. - * Here we will pick out the magic auth params which arrive as a get request, - * and the only communications to arrive this way. * * Magic Auth * ========== @@ -298,6 +290,8 @@ class Auth { * * version => the zot revision * * delegate => optional urlencoded webbie of a local channel to invoke delegation rights for * + * * test => (optional 1 or 0 - debugs the authentication exchange and returns a json response instead of redirecting the browser session) + * * When this packet is received, an "auth-check" zot message is sent to $mysite. * (e.g. if $_GET['auth'] is foobar@podunk.edu, a zot packet is sent to the podunk.edu zot endpoint, which is typically /post) * If no information has been recorded about the requesting identity a zot information packet will be retrieved before @@ -313,7 +307,8 @@ class Auth { * "guid":"kgVFf_...", * "guid_sig":"PT9-TApz...", * "url":"http:\/\/podunk.edu", - * "url_sig":"T8Bp7j..." + * "url_sig":"T8Bp7j...", + * "sitekey":"aMtgKTiirXrICP..." * }, * "recipients":{ * { @@ -338,6 +333,7 @@ class Auth { * "confirm":"q0Ysovd1u...", * "service_class":(optional) * "level":(optional) + * "DNT": (optional do-not-track - 1 or 0) * } * \endcode * -- cgit v1.2.3 From 8e1e301764058f3bf444f0e448a184614e729159 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Wed, 9 Dec 2015 21:03:29 -0800 Subject: refactor a few more redundant returns --- Zotlabs/Zot/Auth.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php index f9a1de8ab..07879fbd9 100644 --- a/Zotlabs/Zot/Auth.php +++ b/Zotlabs/Zot/Auth.php @@ -158,22 +158,19 @@ class Auth { $this->Debug('packet contents: ' . $p); $result = zot_zot($hubloc['hubloc_callback'],$p); - if(! $result['success']) { logger('auth_check callback failed.'); - if($this->test) { + if($this->test) $this->Debug('auth check request to your site returned .' . print_r($result, true)); - return false; - } return false; } + $j = json_decode($result['body'], true); if(! $j) { logger('auth_check json data malformed.'); - if($this->test) { + if($this->test) $this->Debug('json malformed: ' . $result['body']); - return false; - } + return false; } $this->Debug('auth check request returned .' . print_r($j, true)); @@ -185,10 +182,8 @@ class Auth { if (! rsa_verify($this->sec . $hubloc['xchan_hash'],base64url_decode($j['confirm']),$hubloc['xchan_pubkey'])) { logger('final confirmation failed.'); - if($this->test) { + if($this->test) $this->Debug('final confirmation failed. ' . $sec . print_r($j,true) . print_r($hubloc,true)); - return false; - } return false; } @@ -211,7 +206,8 @@ class Auth { $_SESSION['authenticated'] = 1; - // check for delegation + // check for delegation and if all is well, log them in locally with delegation restrictions + $this->delegate_success = false; if($this->delegate) { -- cgit v1.2.3 From f73c82632f213ac7971b54220b4a0c87d354ca1e Mon Sep 17 00:00:00 2001 From: redmatrix Date: Thu, 10 Dec 2015 19:18:55 -0800 Subject: some minor cleanup - change the default of the discover tab (public stream access) --- Zotlabs/Zot/Auth.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'Zotlabs') diff --git a/Zotlabs/Zot/Auth.php b/Zotlabs/Zot/Auth.php index 07879fbd9..fed253923 100644 --- a/Zotlabs/Zot/Auth.php +++ b/Zotlabs/Zot/Auth.php @@ -102,12 +102,8 @@ class Auth { function Verify($channel,$hubloc) { - logger('auth request received from ' . $hubloc['hubloc_addr'] ); - - // check credentials and access - // If they are already authenticated and haven't changed credentials, - // we can save an expensive network round trip and improve performance. + logger('auth request received from ' . $hubloc['hubloc_addr'] ); $this->remote = remote_channel(); $this->remote_service_class = ''; @@ -115,6 +111,11 @@ class Auth { $this->remote_hub = $hubloc['hubloc_url']; $this->dnt = 0; + // check credentials and access + + // If they are already authenticated and haven't changed credentials, + // we can save an expensive network round trip and improve performance. + // Also check that they are coming from the same site as they authenticated with originally. $already_authed = (((remote_channel()) && ($hubloc['hubloc_hash'] == remote_channel()) -- cgit v1.2.3