Esempio n. 1
0
function zot_reply_auth_check($data, $encrypted_packet)
{
    $ret = array('success' => false);
    /*
     * Requestor visits /magic/?dest=somewhere on their own site with a browser
     * magic redirects them to $destsite/post [with auth args....]
     * $destsite sends an auth_check packet to originator site
     * The auth_check packet is handled here by the originator's site 
     * - the browser session is still waiting
     * inside $destsite/post for everything to verify
     * If everything checks out we'll return a token to $destsite
     * and then $destsite will verify the token, authenticate the browser
     * session and then redirect to the original destination.
     * If authentication fails, the redirection to the original destination
     * will still take place but without authentication.
     */
    logger('mod_zot: auth_check', LOGGER_DEBUG);
    if (!$encrypted_packet) {
        logger('mod_zot: auth_check packet was not encrypted.');
        $ret['message'] .= 'no packet encryption' . EOL;
        json_return_and_die($ret);
    }
    $arr = $data['sender'];
    $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
    // garbage collect any old unused notifications
    // This was and should be 10 minutes but my hosting provider has time lag between the DB and
    // 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');
    $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($sender_hash));
    // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in
    // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end.
    // First verify their signature. We will have obtained a zot-info packet from them as part of the sender
    // verification.
    if (!$y || !rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $y[0]['xchan_pubkey'])) {
        logger('mod_zot: auth_check: sender not found or secret_sig invalid.');
        $ret['message'] .= 'sender not found or sig invalid ' . print_r($y, true) . EOL;
        json_return_and_die($ret);
    }
    // There should be exactly one recipient, the original auth requestor
    $ret['message'] .= 'recipients ' . print_r($recipients, true) . EOL;
    if ($data['recipients']) {
        $arr = $data['recipients'][0];
        $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
        $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1", dbesc($recip_hash));
        if (!$c) {
            logger('mod_zot: auth_check: recipient channel not found.');
            $ret['message'] .= 'recipient not found.' . EOL;
            json_return_and_die($ret);
        }
        $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash, $c[0]['channel_prvkey']));
        // 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']);
        if (!$z) {
            logger('mod_zot: auth_check: verification key not found.');
            $ret['message'] .= 'verification key not found' . EOL;
            json_return_and_die($ret);
        }
        $u = q("select account_service_class from account where account_id = %d limit 1", intval($c[0]['channel_account_id']));
        logger('mod_zot: auth_check: success', LOGGER_DEBUG);
        $ret['success'] = true;
        $ret['confirm'] = $confirm;
        if ($u && $u[0]['account_service_class']) {
            $ret['service_class'] = $u[0]['account_service_class'];
        }
        // Set "do not track" flag if this site or this channel's profile is restricted
        // in some way
        if (intval(get_config('system', 'block_public'))) {
            $ret['DNT'] = true;
        }
        if (!perm_is_allowed($c[0]['channel_id'], '', 'view_profile')) {
            $ret['DNT'] = true;
        }
        if (get_pconfig($c[0]['channel_id'], 'system', 'do_not_track')) {
            $ret['DNT'] = true;
        }
        if (get_pconfig($c[0]['channel_id'], 'system', 'hide_online_status')) {
            $ret['DNT'] = true;
        }
        json_return_and_die($ret);
    }
    json_return_and_die($ret);
}
Esempio n. 2
0
function magic_init(&$a)
{
    $ret = array('success' => false, 'url' => '', 'message' => '');
    logger('mod_magic: invoked', LOGGER_DEBUG);
    logger('mod_magic: args: ' . print_r($_REQUEST, true), LOGGER_DATA);
    $addr = x($_REQUEST, 'addr') ? $_REQUEST['addr'] : '';
    $dest = x($_REQUEST, 'dest') ? $_REQUEST['dest'] : '';
    $test = x($_REQUEST, 'test') ? intval($_REQUEST['test']) : 0;
    $rev = x($_REQUEST, 'rev') ? intval($_REQUEST['rev']) : 0;
    $delegate = x($_REQUEST, 'delegate') ? $_REQUEST['delegate'] : '';
    $parsed = parse_url($dest);
    if (!$parsed) {
        if ($test) {
            $ret['message'] .= 'could not parse ' . $dest . EOL;
            return $ret;
        }
        goaway($dest);
    }
    $basepath = $parsed['scheme'] . '://' . $parsed['host'] . ($parsed['port'] ? ':' . $parsed['port'] : '');
    $x = q("select * from hubloc where hubloc_url = '%s' order by hubloc_connected desc limit 1", dbesc($basepath));
    if (!$x) {
        /*
         * We have no records for, or prior communications with this hub. 
         * If an address was supplied, let's finger them to create a hub record. 
         * Otherwise we'll use the special address '[system]' which will return
         * either a system channel or the first available normal channel. We don't
         * really care about what channel is returned - we need the hub information 
         * from that response so that we can create signed auth packets destined 
         * for that hub.
         *
         */
        $ret = zot_finger($addr ? $addr : '[system]@' . $parsed['host'], null);
        if ($ret['success']) {
            $j = json_decode($ret['body'], true);
            if ($j) {
                import_xchan($j);
            }
            // Now try again
            $x = q("select * from hubloc where hubloc_url = '%s' order by hubloc_connected desc limit 1", dbesc($basepath));
        }
    }
    if (!$x) {
        if ($rev) {
            goaway($dest);
        } else {
            logger('mod_magic: no channels found for requested hub.' . print_r($_REQUEST, true));
            if ($test) {
                $ret['message'] .= 'This site has no previous connections with ' . $basepath . EOL;
                return $ret;
            }
            notice(t('Hub not found.') . EOL);
            return;
        }
    }
    // This is ready-made for a plugin that provides a blacklist or "ask me" before blindly authenticating.
    // By default, we'll proceed without asking.
    $arr = array('channel_id' => local_channel(), 'xchan' => $x[0], 'destination' => $dest, 'proceed' => true);
    call_hooks('magic_auth', $arr);
    $dest = $arr['destination'];
    if (!$arr['proceed']) {
        if ($test) {
            $ret['message'] .= 'cancelled by plugin.' . EOL;
            return $ret;
        }
        goaway($dest);
    }
    if (get_observer_hash() && $x[0]['hubloc_url'] === z_root()) {
        // We are already authenticated on this site and a registered observer.
        // Just redirect.
        if ($test) {
            $ret['success'] = true;
            $ret['message'] .= 'Local site - you are already authenticated.' . EOL;
            return $ret;
        }
        $delegation_success = false;
        if ($delegate) {
            $r = q("select * from channel left join hubloc on channel_hash = hubloc_hash where hubloc_addr = '%s' limit 1", dbesc($delegate));
            if ($r && intval($r[0]['channel_id'])) {
                $allowed = perm_is_allowed($r[0]['channel_id'], get_observer_hash(), 'delegate');
                if ($allowed) {
                    $_SESSION['delegate_channel'] = $r[0]['channel_id'];
                    $_SESSION['delegate'] = get_observer_hash();
                    $_SESSION['account_id'] = intval($r[0]['channel_account_id']);
                    change_channel($r[0]['channel_id']);
                    $delegation_success = true;
                }
            }
        }
        // FIXME: check and honour local delegation
        goaway($dest);
    }
    if (local_channel()) {
        $channel = App::get_channel();
        $token = random_string();
        $token_sig = base64url_encode(rsa_sign($token, $channel['channel_prvkey']));
        $channel['token'] = $token;
        $channel['token_sig'] = $token_sig;
        Zotlabs\Zot\Verify::create('auth', $channel['channel_id'], $token, $x[0]['hubloc_url']);
        $target_url = $x[0]['hubloc_callback'] . '/?f=&auth=' . urlencode($channel['channel_address'] . '@' . App::get_hostname()) . '&sec=' . $token . '&dest=' . urlencode($dest) . '&version=' . ZOT_REVISION;
        if ($delegate) {
            $target_url .= '&delegate=' . urlencode($delegate);
        }
        logger('mod_magic: redirecting to: ' . $target_url, LOGGER_DEBUG);
        if ($test) {
            $ret['success'] = true;
            $ret['url'] = $target_url;
            $ret['message'] = 'token ' . $token . ' created for channel ' . $channel['channel_id'] . ' for url ' . $x[0]['hubloc_url'] . EOL;
            return $ret;
        }
        goaway($target_url);
    }
    if ($test) {
        $ret['message'] = 'Not authenticated or invalid arguments to mod_magic' . EOL;
        return $ret;
    }
    goaway($dest);
}