diff --git a/Zotlabs/Module/Magic.php b/Zotlabs/Module/Magic.php index 122d90b1b..6aba9b1ba 100644 --- a/Zotlabs/Module/Magic.php +++ b/Zotlabs/Module/Magic.php @@ -6,6 +6,8 @@ use Zotlabs\Web\Controller; use Zotlabs\Web\HTTPSig; use Zotlabs\Lib\Libzot; use Zotlabs\Lib\SConfig; +use GuzzleHttp\Psr7\Request; +use HttpSignature\HttpMessageSigner; class Magic extends Controller { @@ -101,26 +103,65 @@ class Magic extends Controller { $dest = strip_zids($dest); $dest = strip_query_param($dest,'f'); - // We now post to the OWA endpoint. This improves security by providing a signed digest + // try RFC9421 first - $data = json_encode([ 'OpenWebAuth' => random_string() ]); + $request = new Request( + 'GET', + $owapath, + [ + 'Host' => $parsed['host'], + 'Date' => gmdate('D, d M Y H:i:s T'), + 'Accept' => 'application/x-zot+json', + 'X-Open-Web-Auth' => random_string(), + ], + ); - $headers = []; - $headers['Accept'] = 'application/x-zot+json' ; - $headers['Content-Type'] = 'application/x-zot+json' ; - $headers['X-Open-Web-Auth'] = random_string(); - $headers['Host'] = $parsed['host']; - $headers['(request-target)'] = 'get /owa'; + $signer = new HttpMessageSigner(); + + $signer->setPrivateKey($channel['channel_prvkey']); + $signer->setAlgorithm('rsa-v1_5-sha256'); + $signer->setKeyId(channel_url($channel)); + $signer->setCreated(time()); + $signer->setExpires(time() + 3600); + + $coveredFields = '("@method" "@target-uri" "host" "date" "accept" "x-open-web-auth")'; + $request = $signer->signRequest($coveredFields, $request); + $signedHeaders = $signer->getHeaders($request); + + $curlHeaders = []; + foreach ($signedHeaders as $key => $value) { + $curlHeaders[] = $key . ': ' . $value; + } - $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); $redirects = 0; + $x = z_fetch_url($owapath, false, $redirects, ['headers' => $curlHeaders]); + logger('owa RFC9421 fetch returned: ' . print_r($x,true),LOGGER_DATA); - $x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]); + $rfc9421 = false; - logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA); + if ($x['success']) { + $rfc9421_result = json_decode($x['body'], true); + $rfc9421 = $rfc9421_result['success']; + } + + if (!$rfc9421 || ($x['return_code'] >= 400 && $x['return_code'] != 404)) { + $headers = []; + $headers['Accept'] = 'application/x-zot+json' ; + $headers['Content-Type'] = 'application/x-zot+json' ; + $headers['X-Open-Web-Auth'] = random_string(); + $headers['Host'] = $parsed['host']; + $headers['(request-target)'] = 'get /owa'; + + $headers = HTTPSig::create_sig($headers,$channel['channel_prvkey'], channel_url($channel),true,'sha512'); + $redirects = 0; + + $x = z_fetch_url($owapath, false, $redirects, ['headers' => $headers]); + logger('owa fetch returned: ' . print_r($x,true),LOGGER_DATA); + } if ($x['success']) { $j = json_decode($x['body'],true); + if ($j['success'] && $j['encrypted_token']) { // decrypt the token using our private key $token = ''; @@ -139,7 +180,6 @@ class Magic extends Controller { echo $o; killme(); - } } } diff --git a/Zotlabs/Module/Owa.php b/Zotlabs/Module/Owa.php index 254b1c7d1..dab2ea5fe 100644 --- a/Zotlabs/Module/Owa.php +++ b/Zotlabs/Module/Owa.php @@ -19,96 +19,128 @@ use Zotlabs\Web\Controller; class Owa extends Controller { public function init(): void - { + { $ret = [ 'success' => false ]; - if (!$this->validateAuthorizationHeader()) { - $this->error('Missing or invalid authorization header.'); + // try OpenWebAuth over RFC9421 + + $sigdata = HTTPSig::verify(EMPTY_STR); + + if ($sigdata && $sigdata['portable_id'] && $sigdata['header_valid']) { + $portable_id = $sigdata['portable_id']; + + if (!check_channelallowed($portable_id)) { + json_return_and_die($ret, 'application/x-zot+json'); + } + + if (!check_siteallowed($sigdata['signer'])) { + json_return_and_die($ret, 'application/x-zot+json'); + } + + $hubs = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE hubloc_hash = '%s' ORDER BY hubloc_id DESC", + dbesc($portable_id) + ); + + if ($hubs) { + logger('OWA RFC9421 success: ' . $hubs[0]['hubloc_id_url'], LOGGER_DATA); + $ret['success'] = true; + $token = random_string(32); + Verify::create('owt', 0, $token, $hubs[0]['hubloc_id_url']); + $result = ''; + openssl_public_encrypt($token, $result, $hubs[0]['xchan_pubkey']); + $ret['encrypted_token'] = base64url_encode($result); + } } - - $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_REMOTE_USER']; - - $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); - if ($sigblock) { - $keyId = $sigblock['keyId']; - $parsed = parse_url($keyId); - if (str_starts_with($parsed['scheme'],'http')) { - unset($parsed['fragment']); - unset($parsed['query']); - $keyId = unparse_url($parsed); + else { + if (!$this->validateAuthorizationHeader()) { + $this->error('Missing or invalid authorization header.'); } - else { - $keyId = str_replace('acct:', '', $keyId); - } - if ($keyId) { - $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') - AND hubloc_deleted = 0 AND xchan_pubkey != '' - ORDER BY hubloc_id DESC", - dbesc($keyId), - dbesc($keyId), - dbesc($keyId) - ); - if (! $r) { - $found = discover_by_webbie($keyId); - logger('found = ' . print_r($found, true)); - if ($found) { - $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ", - dbesc($keyId), - dbesc($keyId), - dbesc($keyId) - ); - } + + $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_REMOTE_USER']; + + $sigblock = HTTPSig::parse_sigheader($_SERVER['HTTP_AUTHORIZATION']); + if ($sigblock) { + $keyId = $sigblock['keyId']; + $parsed = parse_url($keyId); + if (str_starts_with($parsed['scheme'],'http')) { + unset($parsed['fragment']); + unset($parsed['query']); + $keyId = unparse_url($parsed); } - - if ($r) { - foreach ($r as $hubloc) { - $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); - if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { - logger('OWA header: ' . print_r($verified,true),LOGGER_DATA); - logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA); - $ret['success'] = true; - $token = random_string(32); - Verify::create('owt',0,$token,$hubloc['hubloc_id_url']); - $result = ''; - openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); - $ret['encrypted_token'] = base64url_encode($result); - break; - } else { - logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); + else { + $keyId = str_replace('acct:', '', $keyId); + } + if ($keyId) { + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') + AND hubloc_deleted = 0 AND xchan_pubkey != '' + ORDER BY hubloc_id DESC", + dbesc($keyId), + dbesc($keyId), + dbesc($keyId) + ); + if (! $r) { + $found = discover_by_webbie($keyId); + logger('found = ' . print_r($found, true)); + if ($found) { + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s' OR xchan_hash = '%s') AND hubloc_deleted = 0 AND xchan_pubkey != '' ORDER BY hubloc_id DESC ", + dbesc($keyId), + dbesc($keyId), + dbesc($keyId) + ); } } - if (!$ret['success']) { + if ($r) { + foreach ($r as $hubloc) { + $verified = HTTPSig::verify(file_get_contents('php://input'), $hubloc['xchan_pubkey']); + if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { + logger('OWA header: ' . print_r($verified,true),LOGGER_DATA); + logger('OWA success: ' . $hubloc['hubloc_id_url'],LOGGER_DATA); + $ret['success'] = true; + $token = random_string(32); + Verify::create('owt',0,$token,$hubloc['hubloc_id_url']); + $result = ''; + openssl_public_encrypt($token,$result,$hubloc['xchan_pubkey']); + $ret['encrypted_token'] = base64url_encode($result); + break; + } else { + logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); + } + } - // Possible a reinstall? - // In this case we probably already have an old hubloc - // but not the new one yet. + if (!$ret['success']) { - $found = discover_by_webbie($keyId); + // Possible a reinstall? + // In this case we probably already have an old hubloc + // but not the new one yet. - if ($found) { - $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash - WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1", - dbesc(str_replace('acct:', '', $keyId)), - dbesc($keyId) - ); + $found = discover_by_webbie($keyId); - if ($r) { - $verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']); - if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { - logger('OWA header: ' . print_r($verified,true), LOGGER_DATA); - logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA); - $ret['success'] = true; - $token = random_string(32); - Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']); - $result = ''; - openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']); - $ret['encrypted_token'] = base64url_encode($result); - } else { - logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); + if ($found) { + $r = q("SELECT * FROM hubloc LEFT JOIN xchan ON hubloc_hash = xchan_hash + WHERE (hubloc_addr = '%s' OR hubloc_id_url = '%s') AND hubloc_deleted = 0 ORDER BY hubloc_id DESC LIMIT 1", + dbesc(str_replace('acct:', '', $keyId)), + dbesc($keyId) + ); + + if ($r) { + $verified = HTTPSig::verify(file_get_contents('php://input'), $r[0]['xchan_pubkey']); + if ($verified && $verified['header_signed'] && $verified['header_valid'] && ($verified['content_valid'] || (! $verified['content_signed']))) { + logger('OWA header: ' . print_r($verified,true), LOGGER_DATA); + logger('OWA success: ' . $r[0]['hubloc_id_url'], LOGGER_DATA); + $ret['success'] = true; + $token = random_string(32); + Verify::create('owt', 0, $token, $r[0]['hubloc_id_url']); + $result = ''; + openssl_public_encrypt($token, $result, $r[0]['xchan_pubkey']); + $ret['encrypted_token'] = base64url_encode($result); + } else { + logger('OWA fail: ' . $hubloc['hubloc_id'] . ' ' . $hubloc['hubloc_id_url']); + } } } }