magic auth - it's mostly done or at least all the code bits are written and it looks in theory to be pretty secure and it doesn't white screen. Getting it to actually work(?), well we won't know how hard that will be until we get it on a couple of systems and try it. Magic auth on one box is a no-op because you're already authenticated.

This commit is contained in:
friendica 2013-01-21 19:16:21 -08:00
parent 6421c09cff
commit 5949607d17
5 changed files with 232 additions and 79 deletions

View file

@ -454,7 +454,7 @@ if(! class_exists('App')) {
private $widgets = array(); // widgets for this page
public $groups;
public $language;
public $module_loaded = false;
public $query_string;
@ -749,6 +749,13 @@ if(! class_exists('App')) {
$this->apps = $arr;
}
function set_groups($g) {
$this->groups = $g;
}
function get_groups() {
return $this->groups;
}
function set_widget($title,$html, $location = 'aside') {
$this->widgets[] = array('title' => $title, 'html' => $html, 'location' => $location);

View file

@ -64,14 +64,19 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p
goaway(z_root());
}
// if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) {
// $r = q("SELECT * FROM `contact` WHERE `id` = %d LIMIT 1",
// intval($_SESSION['visitor_id'])
// );
// if(count($r)) {
// $a->contact = $r[0];
// }
// }
if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) {
$r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_addr = '%s' limit 1",
dbesc($_SESSION['visitor_id'])
);
if($r) {
get_app()->set_observer($r[0]);
}
else {
unset($_SESSION['visitor_id']);
unset($_SESSION['authenticated']);
}
$a->set_groups(init_groups_visitor($_SESSION['visitor_id']));
}
if(x($_SESSION,'uid') || x($_SESSION,'account_id')) {

View file

@ -101,18 +101,13 @@ function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_
'version' => ZOT_REVISION
);
// These fields are present when using magic auth
if(array_key_exists('token',$channel)) {
$data['sender']['token'] = $channel['token'];
$data['sender']['token_sig'] = $channel['token_sig'];
}
if($recipients)
$data['recipients'] = $recipients;
if($secret)
if($secret) {
$data['secret'] = $secret;
$data['secret_sig'] = base64url_encode(rsa_sign($secret,$channel['channel_prvkey']));
}
logger('zot_build_packet: ' . print_r($data,true));

View file

@ -4,58 +4,74 @@
function magic_init(&$a) {
$url = ((x($_REQUEST,'url')) ? $_REQUEST['url'] : '');
$addr = ((x($_REQUEST,'addr')) ? $_REQUEST['addr'] : '');
$hash = ((x($_REQUEST,'hash')) ? $_REQUEST['hash'] : '');
$dest = ((x($_REQUEST,'dest')) ? $_REQUEST['dest'] : '');
if($hash) {
$x = q("select xchan.xchan_url, hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash
where hublock_hash = '%s' and (hubloc_flags & %d) limit 1",
intval(HUBLOC_FLAGS_PRIMARY)
);
}
elseif($addr) {
$x = q("select hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash
where xchan_addr = '%s' and (hubloc_flags & %d) limit 1",
dbesc($addr),
intval(HUBLOC_FLAGS_PRIMARY)
);
}
if(local_user()) {
if(! $x) {
if($hash) {
$x = q("select xchan.xchan_url, hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash
where hublock_hash = '%s' and (hubloc_flags & %d) limit 1",
intval(HUBLOC_FLAGS_PRIMARY)
);
}
elseif($addr) {
$x = q("select hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash
where xchan_addr = '%s' and (hubloc_flags & %d) limit 1",
dbesc($addr),
intval(HUBLOC_FLAGS_PRIMARY)
);
}
// Finger them if they've never been seen here before
if(! $x) {
notice( t('Channel not found.') . EOL);
return;
}
if($x[0]['hubloc_url'] === z_root()) {
$webbie = substr($x[0]['hubloc_addr'],0,strpos('@',$x[0]['hubloc_addr']));
switch($dest) {
case 'channel':
$desturl = z_root() . '/channel/' . $webbie;
break;
case 'photos':
$desturl = z_root() . '/photos/' . $webbie;
break;
case 'profile':
$desturl = z_root() . '/profile/' . $webbie;
break;
default:
$desturl = $dest;
break;
if($addr) {
$ret = zot_finger($addr,null);
if($ret['success']) {
$j = json_decode($ret['body'],true);
if($j)
import_xchan($j);
$x = q("select hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash
where xchan_addr = '%s' and (hubloc_flags & %d) limit 1",
dbesc($addr),
intval(HUBLOC_FLAGS_PRIMARY)
);
}
// We are already authenticated on this site and a registered observer.
// Just redirect.
goaway($desturl);
}
}
$channel = $a->get_channel();
if(! $x) {
notice( t('Channel not found.') . EOL);
return;
}
if($x[0]['hubloc_url'] === z_root()) {
$webbie = substr($x[0]['hubloc_addr'],0,strpos('@',$x[0]['hubloc_addr']));
switch($dest) {
case 'channel':
$desturl = z_root() . '/channel/' . $webbie;
break;
case 'photos':
$desturl = z_root() . '/photos/' . $webbie;
break;
case 'profile':
$desturl = z_root() . '/profile/' . $webbie;
break;
default:
$desturl = $dest;
break;
}
// We are already authenticated on this site and a registered observer.
// Just redirect.
goaway($desturl);
}
if(local_user()) {
$channel = $a->get_channel();
$token = random_string();
$token_sig = rsa_sign($token,$channel['channel_prvkey']);
$token_sig = base64url_encode(rsa_sign($token,$channel['channel_prvkey']));
$channel['token'] = $token;
$channel['token_sig'] = $token_sig;
@ -73,25 +89,12 @@ function magic_init(&$a) {
dbesc(datetime_convert())
);
$packet = zot_build_packet($channel,'auth',$recip,$x[0]['hubloc_sitekey'],$hash);
$result = zot_zot($x[0]['hubloc_callback'],$packet);
if($result['success']) {
$j = json_decode($result['body'],true);
if($j['iv']) {
$y = aes_unencapsulate($j,$channel['prvkey']);
$j = json_decode($y,true);
}
if($j['token'] && $j['ticket'] && $j['token'] === $token) {
$r = q("delete from verify where token = '%s' and type = '%s' and channel = %d limit 1",
dbesc($token),
dbesc('auth'),
intval($channel['channel_id'])
);
goaway($x[0]['callback'] . '?f=&ticket=' . $ticket . '&dest=' . $dest);
}
}
goaway($dest);
goaway($x[0]['hubloc_callback'] . '/' . substr($x[0]['hubloc_addr'],0,strpos($x[0]['hubloc_addr'],'@'))
. '/?f=&auth=' . $channel['channel_address'] . '@' . $a->get_hostname()
. '&sec=' . $token . '&dest=' . $dest . '&version=' . ZOT_REVISION);
}
if(strpos($dest,'/'))
goaway($dest);
goaway(z_root());
}

View file

@ -6,6 +6,110 @@
require_once('include/zot.php');
function post_init(&$a) {
// All other access to this endpoint is via the post method.
// Here we will pick out the magic auth params which arrive
// as a get request.
if(argc() > 1) {
$webbie = argv(1);
if(array_key_exists('auth',$_REQUEST)) {
$address = $_REQUEST['auth'];
$dest = $_REQUEST['dest'];
$sec = $_REQUEST['sec'];
$version = $_REQUEST['version'];
switch($dest) {
case 'channel':
$desturl = z_root() . '/channel/' . $webbie;
break;
case 'photos':
$desturl = z_root() . '/photos/' . $webbie;
break;
case 'profile':
$desturl = z_root() . '/profile/' . $webbie;
break;
default:
$desturl = $dest;
break;
}
$c = q("select * from channel where channel_address = '%s' limit 1",
dbesc($webbie)
);
if(! $c) {
logger('mod_zot: auth: unable to find channel ' . $webbie);
// They'll get a notice when they hit the page, we don't need two.
goaway($desturl);
}
// 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' limit 1",
dbesc($address)
);
if(! $x) {
// finger them if they can't be found.
$ret = zot_finger($addr,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' limit 1",
dbesc($address)
);
}
}
if(! $x)
goaway($desturl);
// check credentials and access
// Auth packets MUST use ultra top-secret hush-hush mode
$p = zot_build_packet($c[0],$type = 'auth_check',array('guid' => $x[0]['hubloc_guid'],'guid_sig' => $x[0]['hubloc_guid_sig']), $x[0]['hubloc_prvkey'], $sec);
$result = zot_zot($x[0]['hubloc_url'],$p);
if($result['success']) {
$j = json_decode($result['body'],true);
if($j['result']) {
// everything is good... maybe
if(local_user()) {
notice( t('Remote authentication blocked. You are logged into this site locally. Please logout and retry') . EOL);
goaway($desturl);
}
// log them in
$_SESSION['authenticated'] = 1;
$_SESSION['visitor_id'] = $x[0]['xchan_hash'];
$a->set_observer($x[0]);
$a->set_groups(init_groups_visitor($_SESSION['visitor_id']));
notice(sprintf( t('Welcome %s. Remote authentication successful.'),$x[0]['xchan_name']));
}
}
goaway($desturl);
}
logger('mod_zot: invalid args: ' . print_r($a->argv,true));
killme();
}
return;
}
function post_post(&$a) {
@ -165,12 +269,51 @@ function post_post(&$a) {
}
if($msgtype === 'auth') {
logger('mod_post: auth: ' . print_r($data,true));
if($msgtype === 'auth_check') {
$arr = $data['sender'];
$sender_hash = base64url_encode(hash('whirlpool',$arr['guid'] . $arr['guid_sig'], true));
$y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1",
dbesc($sender_hash)
);
if((! $y) || (! rsa_verify($data['secret'],$data['secret_sig'],$y[0]['xchan_pubkey']))) {
logger('mod_zot: auth_check: sender not found or secret_sig invalid.');
json_return_and_die($ret);
}
if($data['recipients']) {
$arr = $data['recipients'][0];
$recip_hash = base64url_encode(hash('whirlpool',$arr['guid'] . $arr['guid_sig'], true));
$c = q("select channel_id from channel where channel_hash = '%s' limit 1",
dbesc($recip_hash)
);
if(! $c) {
logger('mod_zot: auth_check: recipient channel not found.');
json_return_and_die($ret);
}
$z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' limit 1",
intval($c[0]['channel_id']),
dbesc($data['secret'])
);
if(! $z) {
logger('mod_zot: auth_check: verification key not found.');
json_return_and_die($ret);
}
$r = q("delete from verify where id = %d limit 1",
intval($z[0]['id'])
);
$ret['result'] = true;
json_return_and_die($ret);
}
json_return_and_die($ret);
}
// catchall
json_return_and_die($ret);
}