function post()
 {
     $hash = $_POST['hash'];
     $time = $_POST['time'];
     $sig = $_POST['signature'];
     $resource = $_POST['resource'];
     $revision = intval($_POST['revision']);
     if (!$hash) {
         killme();
     }
     $channel = channelx_by_hash($hash);
     if (!$channel || !$time || !$sig) {
         killme();
     }
     $slop = intval(get_pconfig($channel['channel_id'], 'system', 'getfile_time_slop'));
     if ($slop < 1) {
         $slop = 3;
     }
     $d1 = datetime_convert('UTC', 'UTC', "now + {$slop} minutes");
     $d2 = datetime_convert('UTC', 'UTC', "now - {$slop} minutes");
     if ($time > $d1 || $time < $d2) {
         logger('time outside allowable range');
         killme();
     }
     if (!rsa_verify($hash . '.' . $time, base64url_decode($sig), $channel['channel_pubkey'])) {
         logger('verify failed.');
         killme();
     }
     $r = attach_by_hash($resource, $revision);
     if (!$r['success']) {
         notice($r['message'] . EOL);
         return;
     }
     $unsafe_types = array('text/html', 'text/css', 'application/javascript');
     if (in_array($r['data']['filetype'], $unsafe_types)) {
         header('Content-type: text/plain');
     } else {
         header('Content-type: ' . $r['data']['filetype']);
     }
     header('Content-disposition: attachment; filename="' . $r['data']['filename'] . '"');
     if (intval($r['data']['os_storage'])) {
         $fname = dbunescbin($r['data']['data']);
         if (strpos($fname, 'store') !== false) {
             $istream = fopen($fname, 'rb');
         } else {
             $istream = fopen('store/' . $channel['channel_address'] . '/' . $fname, 'rb');
         }
         $ostream = fopen('php://output', 'wb');
         if ($istream && $ostream) {
             pipe_streams($istream, $ostream);
             fclose($istream);
             fclose($ostream);
         }
     } else {
         echo dbunescbin($r['data']['data']);
     }
     killme();
 }
Exemple #2
0
 function Verify($channel, $hubloc)
 {
     logger('auth request received from ' . $hubloc['hubloc_addr']);
     $this->remote = remote_channel();
     $this->remote_service_class = '';
     $this->remote_level = 0;
     $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() && $hubloc['hubloc_url'] === $_SESSION['remote_hub'] ? true : false;
     if ($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) {
         $already_authed = false;
     }
     if ($already_authed) {
         return true;
     }
     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 (\App::$channel['channel_hash'] == $hubloc['xchan_hash']) {
             return true;
         } 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->Debug('already logged in locally with a conflicting identity.');
                 return false;
             }
         }
         return 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
     $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->Debug('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']);
     $this->Debug('packet contents: ' . $p);
     $result = zot_zot($hubloc['hubloc_callback'], $p);
     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;
     }
     $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;
     }
     $this->Debug('auth check request returned .' . print_r($j, true));
     if (!$j['success']) {
         return false;
     }
     // 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->Debug('final confirmation failed. ' . $sec . print_r($j, true) . print_r($hubloc, true));
         }
         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 and if all is well, log them in locally with delegation restrictions
     $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;
             }
         }
     }
     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);
     \App::set_observer($hubloc);
     require_once 'include/security.php';
     \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;
 }
Exemple #3
0
include_once "log.php";
include_once "recharge.php";
include_once "ssl.php";
$config = (include "config.php");
header("Content-type: text/html; charset=utf-8");
log::init('./log', 'itools_log');
$uri = $_SERVER['REQUEST_URI'];
$body = file_get_contents('php://input');
log::instance()->debug("new con: {$uri} {$body}");
$AppID = "533";
$pf_info = $config["itools"][$AppID];
// RSA verify
$notify_data = base64_decode(stripslashes($_POST["notify_data"]));
$notify_data = publickey_decodeing_sectionalized($notify_data, 128, $pf_info["PubKey"]);
$sign = base64_decode(stripslashes($_POST["sign"]));
if (!rsa_verify($notify_data, $sign, $pf_info["PubKey"])) {
    log::instance()->error("ret: 签名无效");
    echo "fail";
    exit;
}
// end verify
echo "success";
$notify_data = json_decode($notify_data, true);
if ($notify_data["result"] != "success") {
    log::instance()->error("ret: 支付失败");
    exit;
}
$note = json_decode(base64_decode($notify_data["order_id_com"]), true);
$ret = recharge($pf_info["PF"], $note["sid"], $note["odr"], $note["uid"], $note["item"], $notify_data["amount"], $notify_data["order_id"], 0);
log::instance()->debug("ret: " . $ret);
 private function getSign($type, $data)
 {
     $md5str = "";
     switch ($type) {
         case "gfb":
             $signarray = array("version", "tranCode", "merchantID", "merOrderNum", "tranAmt", "feeAmt", "tranDateTime", "frontMerUrl", "backgroundMerUrl", "orderId", "gopayOutOrderId", "tranIP", "respCode", "gopayServerTime");
             foreach ($signarray as $v) {
                 if (!isset($data[$v])) {
                     $md5str .= "{$v}=[]";
                 } else {
                     $md5str .= "{$v}=[{$data[$v]}]";
                 }
             }
             $md5str .= "VerficationCode=[" . $this->payConfig['guofubao']['VerficationCode'] . "]";
             $md5str = md5($md5str);
             return $md5str;
             break;
         case "ips":
             $md5str = "billno" . $data['Billno'] . "currencytype" . $data['Currency_Type'] . "amount" . $data['Amount'] . "date" . $data['Date'] . "orderencodetype" . $data['OrderEncodeType'];
             $md5str .= $this->payConfig['ips']['MerKey'];
             $md5str = md5($md5str);
             return $md5str;
             break;
         case "ips_return":
             $md5str = "billno" . $data['billno'] . "currencytype" . $data['Currency_type'] . "amount" . $data['amount'] . "date" . $data['date'] . "succ" . $data['succ'] . "ipsbillno" . $data['ipsbillno'] . "retencodetype" . $data['retencodetype'];
             $md5str .= $this->payConfig['ips']['MerKey'];
             $md5str = md5($md5str);
             return $md5str;
             break;
         case "chinabank":
             $signarray = array("v_amount", "v_moneytype", "v_oid", "v_mid", "v_url");
             foreach ($signarray as $v) {
                 if (!isset($data[$v])) {
                     $md5str .= "";
                 } else {
                     $md5str .= "{$data[$v]}";
                 }
             }
             $md5str .= $this->payConfig['chinabank']['mkey'];
             $md5str = md5($md5str);
             return $md5str;
             break;
         case "chinabank_return":
             $signarray = array("v_oid", "v_pstatus", "v_amount", "v_moneytype");
             foreach ($signarray as $v) {
                 if (!isset($data[$v])) {
                     $md5str .= "";
                 } else {
                     $md5str .= "{$data[$v]}";
                 }
             }
             $md5str .= $this->payConfig['chinabank']['mkey'];
             $md5str = md5($md5str);
             return $md5str;
             break;
             /*case "baofoo"://老宝付支付接口
             			$signarray = array( "MerchantID", "PayID", "TradeDate", "TransID", "OrderMoney", "Merchant_url", "Return_url", "NoticeType" );
             			foreach ( $signarray as $v )
             			{
             				$md5str .= $data[$v];
             			}
             			$md5str .= $this->payConfig['baofoo']['pkey'];
             			$md5str = md5( $md5str );
             			return $md5str;
             		break;
             		case "baofoo_return":
             			$signarray = array( "MerchantID", "TransID", "Result", "resultDesc", "factMoney", "additionalInfo", "SuccTime" );
             			foreach ( $signarray as $v )
             			{
             				$md5str .= $data[$v];
             			}
             			$md5str .= $this->payConfig['baofoo']['pkey'];
             			$md5str = md5( $md5str );
             			return $md5str;
             		break;*/
         /*case "baofoo"://老宝付支付接口
         			$signarray = array( "MerchantID", "PayID", "TradeDate", "TransID", "OrderMoney", "Merchant_url", "Return_url", "NoticeType" );
         			foreach ( $signarray as $v )
         			{
         				$md5str .= $data[$v];
         			}
         			$md5str .= $this->payConfig['baofoo']['pkey'];
         			$md5str = md5( $md5str );
         			return $md5str;
         		break;
         		case "baofoo_return":
         			$signarray = array( "MerchantID", "TransID", "Result", "resultDesc", "factMoney", "additionalInfo", "SuccTime" );
         			foreach ( $signarray as $v )
         			{
         				$md5str .= $data[$v];
         			}
         			$md5str .= $this->payConfig['baofoo']['pkey'];
         			$md5str = md5( $md5str );
         			return $md5str;
         		break;*/
         case "baofoo":
             $signarray = array("MemberID", "PayID", "TradeDate", "TransID", "OrderMoney", "PageUrl", "ReturnUrl", "NoticeType");
             foreach ($signarray as $v) {
                 $md5str .= $data[$v] . '|';
             }
             $md5str .= $this->payConfig['baofoo']['pkey'];
             $md5str = md5($md5str);
             return $md5str;
             break;
         case "baofoo_return":
             $signarray = array("MemberID", "TerminalID", "TransID", "Result", "ResultDesc", "FactMoney", "AdditionalInfo", 'SuccTime');
             foreach ($signarray as $v) {
                 $md5str .= "{$v}" . '=' . $data[$v] . '~|~';
             }
             //dump($md5str);
             $md5str .= 'Md5Sign=' . $this->payConfig['baofoo']['pkey'];
             $md5str = md5($md5str);
             return $md5str;
             break;
         case "shengpay":
             $signarray = array('Name', 'Version', 'Charset', 'MsgSender', 'SendTime', 'OrderNo', 'OrderAmount', 'OrderTime', 'PayType', 'PageUrl', 'NotifyUrl', 'ProductName', 'BuyerContact', 'BuyerIp', 'Ext1', 'Ext2', 'SignType');
             foreach ($signarray as $v) {
                 if (!isset($data[$v])) {
                     $md5str .= "";
                 } else {
                     $md5str .= "{$data[$v]}";
                 }
             }
             $md5str .= $this->payConfig['shengpay']['pkey'];
             //MD5密钥
             $md5str = strtoupper(md5($md5str));
             return $md5str;
             break;
         case "shengpay_return":
             $signarray = array('Name', 'Version', 'Charset', 'TraceNo', 'MsgSender', 'SendTime', 'InstCode', 'OrderNo', 'OrderAmount', 'TransNo', 'TransAmount', 'TransStatus', 'TransType', 'TransTime', 'MerchantNo', 'ErrorCode', 'ErrorMsg', 'Ext1', 'Ext2', 'SignType');
             foreach ($signarray as $v) {
                 if (!isset($data[$v])) {
                     $md5str .= "";
                 } else {
                     $md5str .= "{$data[$v]}";
                 }
             }
             $md5str .= $this->payConfig['shengpay']['mkey'];
             $md5str = strtoupper(md5($md5str));
             return $md5str;
             break;
         case "tenpay":
             $signPars = "";
             ksort($data);
             foreach ($data as $k => $v) {
                 if ("" != $v && "sign" != $k) {
                     $signPars .= $k . "=" . $v . "&";
                 }
             }
             $signPars .= "key=" . $this->payConfig['tenpay']['key'];
             $md5str = strtoupper(md5($signPars));
             return $md5str;
             break;
         case "ecpss":
             $signarray = array('MerNo', 'BillNo', 'Amount', 'ReturnURL');
             //校验源字符串
             foreach ($signarray as $v) {
                 if (!isset($data[$v])) {
                     $md5str .= "";
                 } else {
                     $md5str .= $data[$v];
                 }
             }
             $md5str .= $this->payConfig['ecpss']['MD5key'];
             //MD5密钥
             $md5str = strtoupper(md5($md5str));
             return $md5str;
             break;
         case "ecpss_return":
             $signarray = array("BillNo", "Amount", "Succeed");
             //校验源字符串
             foreach ($signarray as $v) {
                 $md5str .= $data[$v] . "&";
             }
             $md5str .= $this->payConfig['ecpss']['MD5key'];
             $md5str = strtoupper(md5($md5str));
             return $md5str;
             break;
         case "easypay":
             //易生支付
             $para = array();
             while (list($key, $val) = each($data)) {
                 if ($key == "sign" || $key == "sign_type" || $val == "") {
                     continue;
                 } else {
                     $para[$key] = $data[$key];
                 }
             }
             ksort($para);
             reset($para);
             $signPars = "";
             while (list($key, $val) = each($para)) {
                 $signPars .= $key . "=" . $val . "&";
             }
             $signPars = substr($signPars, 0, count($signPars) - 2);
             //去掉最后一个&字符
             $signPars .= $this->payConfig['easypay']['key'];
             $md5str = md5($signPars);
             return $md5str;
             break;
         case "cmpay":
             //中国移动
             $signarray = array('merchantId', 'payNo', 'returnCode', 'message', 'signType', 'type', 'version', 'amount', 'amtItem', 'bankAbbr', 'mobile', 'orderId', 'payDate', 'accountDate', 'reserved1', 'reserved2', 'status', 'orderDate', 'fee');
             foreach ($signarray as $v) {
                 $mac .= $data[$v];
             }
             $signKey = $this->payConfig['cmpay']['serverCert'];
             $mac = MD5sign($signKey, $mac);
             return $mac;
             break;
         case "cmpay_return":
             //中国移动
             foreach ($data as $v) {
                 $mac .= $v;
             }
             $signKey = $this->payConfig['cmpay']['serverCert'];
             //MD5方式签名
             $hmac = MD5sign($signKey, $mac);
             return $hmac;
             break;
         case "allinpay":
             $signarray = array("inputCharset", "pickupUrl", "receiveUrl", "version", "language", "signType", "merchantId", "payerName", "payerEmail", "payerTelephone", "payerIDCard", "pid", "orderNo", "orderAmount", "orderCurrency", "orderDatetime", "orderExpireDatetime", "productName", "productPrice", "productNum", "productId", "productDescription", "ext1", "ext2", "payType", "issuerId", "pan");
             $i = 0;
             foreach ($signarray as $v) {
                 if (0 < $i) {
                     if ($data[$v] !== "") {
                         $md5str .= "&{$v}=" . $data[$v];
                     }
                 } else {
                     if ($data[$v] !== "") {
                         $md5str .= "{$v}=" . $data[$v];
                     }
                 }
                 ++$i;
             }
             $md5str .= "&key=" . $this->payConfig['allinpay']['key'];
             $md5str = strtoupper(md5($md5str));
             return $md5str;
         case "allinpay_return":
             $signarray = array("merchantId", "version", "language", "signType", "payType", "issuerId", "paymentOrderId", "orderNo", "orderDatetime", "orderAmount", "payDatetime", "payAmount", "ext1", "ext2", "payResult", "errorCode", "returnDatetime");
             $i = 0;
             foreach ($signarray as $v) {
                 if (0 < $i) {
                     if ($data[$v] !== "") {
                         $md5str .= "&{$v}=" . $data[$v];
                     }
                 } else {
                     if ($data[$v] !== "") {
                         $md5str .= "{$v}=" . $data[$v];
                     }
                 }
                 ++$i;
             }
             //解析publickey.txt文本获取公钥信息
             require_once C("APP_ROOT") . "Lib/Pay/allinpay/php_rsa.php";
             $publickeyfile = C("APP_ROOT") . "Lib/Pay/allinpay/publickey.txt";
             $publickeycontent = file_get_contents($publickeyfile);
             //echo "<br>".$content;
             $publickeyarray = explode(PHP_EOL, $publickeycontent);
             $publickey = explode('=', $publickeyarray[0]);
             $modulus = explode('=', $publickeyarray[1]);
             $keylength = 1024;
             $verify_result = rsa_verify($md5str, $data['signMsg'], $publickey[1], $modulus[1], $keylength, "sha1");
             return $verify_result;
     }
 }
Exemple #5
0
function check_zotinfo($channel, $locations, &$ret)
{
    //	logger('locations: ' . print_r($locations,true),LOGGER_DATA);
    // This function will likely expand as we find more things to detect and fix.
    // 1. Because magic-auth is reliant on it, ensure that the system channel has a valid hubloc
    //    Force this to be the case if anything is found to be wrong with it.
    // @FIXME ensure that the system channel exists in the first place and has an xchan
    if ($channel['channel_system']) {
        // the sys channel must have a location (hubloc)
        $valid_location = false;
        if (count($locations) === 1 && $locations[0]['primary'] && !$locations[0]['deleted']) {
            if (rsa_verify($locations[0]['url'], base64url_decode($locations[0]['url_sig']), $channel['channel_pubkey']) && $locations[0]['sitekey'] === get_config('system', 'pubkey') && $locations[0]['url'] === z_root()) {
                $valid_location = true;
            } else {
                logger('sys channel: invalid url signature');
            }
        }
        if (!$locations || !$valid_location) {
            logger('System channel locations are not valid. Attempting repair.');
            // Don't trust any existing records. Just get rid of them, but only do this
            // for the sys channel as normal channels will be trickier.
            q("delete from hubloc where hubloc_hash = '%s'", dbesc($channel['channel_hash']));
            $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_primary,\n\t\t\t\thubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey, hubloc_network )\n\t\t\t\tvalues ( '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s' )", dbesc($channel['channel_guid']), dbesc($channel['channel_guid_sig']), dbesc($channel['channel_hash']), dbesc($channel['channel_address'] . '@' . get_app()->get_hostname()), intval(1), dbesc(z_root()), dbesc(base64url_encode(rsa_sign(z_root(), $channel['channel_prvkey']))), dbesc(get_app()->get_hostname()), dbesc(z_root() . '/post'), dbesc(get_config('system', 'pubkey')), dbesc('zot'));
            if ($r) {
                $x = zot_encode_locations($channel);
                if ($x) {
                    $ret['locations'] = $x;
                }
            } else {
                logger('Unable to store sys hub location');
            }
        }
    }
}
Exemple #6
0
function zfinger_init(&$a)
{
    require_once 'include/zot.php';
    require_once 'include/crypto.php';
    $ret = array('success' => false);
    $zhash = x($_REQUEST, 'guid_hash') ? $_REQUEST['guid_hash'] : '';
    $zguid = x($_REQUEST, 'guid') ? $_REQUEST['guid'] : '';
    $zguid_sig = x($_REQUEST, 'guid_sig') ? $_REQUEST['guid_sig'] : '';
    $zaddr = x($_REQUEST, 'address') ? $_REQUEST['address'] : '';
    $ztarget = x($_REQUEST, 'target') ? $_REQUEST['target'] : '';
    $zsig = x($_REQUEST, 'target_sig') ? $_REQUEST['target_sig'] : '';
    $zkey = x($_REQUEST, 'key') ? $_REQUEST['key'] : '';
    $mindate = x($_REQUEST, 'mindate') ? $_REQUEST['mindate'] : '';
    $feed = x($_REQUEST, 'feed') ? intval($_REQUEST['feed']) : 0;
    if ($ztarget) {
        if (!$zkey || !$zsig || !rsa_verify($ztarget, base64url_decode($zsig), $zkey)) {
            logger('zfinger: invalid target signature');
            $ret['message'] = t("invalid target signature");
            json_return_and_die($ret);
        }
    }
    // allow re-written domains so bob@foo.example.com can provide an address of bob@example.com
    // The top-level domain also needs to redirect .well-known/zot-info to the sub-domain with a 301 or 308
    // TODO: Make 308 work in include/network.php for zot_fetch_url and zot_post_url
    if ($zaddr && ($s = get_config('system', 'zotinfo_domainrewrite'))) {
        $arr = explode('^', $s);
        if (count($arr) == 2) {
            $zaddr = str_replace($arr[0], $arr[1], $zaddr);
        }
    }
    $r = null;
    if (strlen($zhash)) {
        $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_hash = '%s' limit 1", dbesc($zhash));
    } elseif (strlen($zguid) && strlen($zguid_sig)) {
        $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash \n\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($zguid), dbesc($zguid_sig));
    } elseif (strlen($zaddr)) {
        if (strpos($zaddr, '[system]') === false) {
            /* normal address lookup */
            $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_address = '%s' or xchan_addr = '%s' ) limit 1", dbesc($zaddr), dbesc($zaddr));
        } else {
            /**
             * The special address '[system]' will return a system channel if one has been defined,
             * Or the first valid channel we find if there are no system channels. 
             *
             * This is used by magic-auth if we have no prior communications with this site - and
             * returns an identity on this site which we can use to create a valid hub record so that
             * we can exchange signed messages. The precise identity is irrelevant. It's the hub
             * information that we really need at the other end - and this will return it.
             *
             */
            $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\twhere ( channel_pageflags & %d )>0 order by channel_id limit 1", intval(PAGE_SYSTEM));
            if (!$r) {
                $r = q("select channel.*, xchan.* from channel left join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere not ( channel_pageflags & %d )>0 order by channel_id limit 1", intval(PAGE_REMOVED));
            }
        }
    } else {
        $ret['message'] = 'Invalid request';
        json_return_and_die($ret);
    }
    if (!$r) {
        $ret['message'] = 'Item not found.';
        json_return_and_die($ret);
    }
    $e = $r[0];
    $id = $e['channel_id'];
    $sys_channel = $e['channel_pageflags'] & PAGE_SYSTEM ? true : false;
    $special_channel = $e['channel_pageflags'] & PAGE_PREMIUM ? true : false;
    $adult_channel = $e['channel_pageflags'] & PAGE_ADULT ? true : false;
    $censored = $e['channel_pageflags'] & PAGE_CENSORED ? true : false;
    $searchable = $e['channel_pageflags'] & PAGE_HIDDEN ? false : true;
    $deleted = $e['xchan_flags'] & XCHAN_FLAGS_DELETED ? true : false;
    if ($deleted || $censored || $sys_channel) {
        $searchable = false;
    }
    $public_forum = false;
    $role = get_pconfig($e['channel_id'], 'system', 'permissions_role');
    if ($role === 'forum') {
        $public_forum = true;
    } else {
        // check if it has characteristics of a public forum based on custom permissions.
        $t = q("select abook_my_perms from abook where abook_channel = %d and (abook_flags & %d)>0 limit 1", intval($e['channel_id']), intval(ABOOK_FLAG_SELF));
        if ($t && $t[0]['abook_my_perms'] & PERMS_W_TAGWALL) {
            $public_forum = true;
        }
    }
    //  This is for birthdays and keywords, but must check access permissions
    $p = q("select * from profile where uid = %d and is_default = 1", intval($e['channel_id']));
    $profile = array();
    if ($p) {
        if (!intval($p[0]['publish'])) {
            $searchable = false;
        }
        $profile['description'] = $p[0]['pdesc'];
        $profile['birthday'] = $p[0]['dob'];
        if ($profile['birthday'] != '0000-00-00' && ($bd = z_birthday($p[0]['dob'], $e['channel_timezone'])) !== '') {
            $profile['next_birthday'] = $bd;
        }
        if ($age = age($p[0]['dob'], $e['channel_timezone'], '')) {
            $profile['age'] = $age;
        }
        $profile['gender'] = $p[0]['gender'];
        $profile['marital'] = $p[0]['marital'];
        $profile['sexual'] = $p[0]['sexual'];
        $profile['locale'] = $p[0]['locality'];
        $profile['region'] = $p[0]['region'];
        $profile['postcode'] = $p[0]['postal_code'];
        $profile['country'] = $p[0]['country_name'];
        $profile['about'] = $p[0]['about'];
        $profile['homepage'] = $p[0]['homepage'];
        $profile['hometown'] = $p[0]['hometown'];
        if ($p[0]['keywords']) {
            $tags = array();
            $k = explode(' ', $p[0]['keywords']);
            if ($k) {
                foreach ($k as $kk) {
                    if (trim($kk, " \t\n\r\v,")) {
                        $tags[] = trim($kk, " \t\n\r\v,");
                    }
                }
            }
            if ($tags) {
                $profile['keywords'] = $tags;
            }
        }
    }
    $ret['success'] = true;
    // Communication details
    $ret['guid'] = $e['xchan_guid'];
    $ret['guid_sig'] = $e['xchan_guid_sig'];
    $ret['key'] = $e['xchan_pubkey'];
    $ret['name'] = $e['xchan_name'];
    $ret['name_updated'] = $e['xchan_name_date'];
    $ret['address'] = $e['xchan_addr'];
    $ret['photo_mimetype'] = $e['xchan_photo_mimetype'];
    $ret['photo'] = $e['xchan_photo_l'];
    $ret['photo_updated'] = $e['xchan_photo_date'];
    $ret['url'] = $e['xchan_url'];
    $ret['connections_url'] = $e['xchan_connurl'] ? $e['xchan_connurl'] : z_root() . '/poco/' . $e['channel_address'];
    $ret['target'] = $ztarget;
    $ret['target_sig'] = $zsig;
    $ret['searchable'] = $searchable;
    $ret['adult_content'] = $adult_channel;
    $ret['public_forum'] = $public_forum;
    if ($deleted) {
        $ret['deleted'] = $deleted;
    }
    // premium or other channel desiring some contact with potential followers before connecting.
    // This is a template - %s will be replaced with the follow_url we discover for the return channel.
    if ($special_channel) {
        $ret['connect_url'] = z_root() . '/connect/' . $e['channel_address'];
    }
    // This is a template for our follow url, %s will be replaced with a webbie
    $ret['follow_url'] = z_root() . '/follow?f=&url=%s';
    $ztarget_hash = $ztarget && $zsig ? make_xchan_hash($ztarget, $zsig) : '';
    $permissions = get_all_perms($e['channel_id'], $ztarget_hash, false);
    if ($ztarget_hash) {
        $permissions['connected'] = false;
        $b = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", dbesc($ztarget_hash), intval($e['channel_id']));
        if ($b) {
            $permissions['connected'] = true;
        }
    }
    $ret['permissions'] = $ztarget && $zkey ? crypto_encapsulate(json_encode($permissions), $zkey) : $permissions;
    if ($permissions['view_profile']) {
        $ret['profile'] = $profile;
    }
    // array of (verified) hubs this channel uses
    $x = zot_encode_locations($e);
    if ($x) {
        $ret['locations'] = $x;
    }
    $ret['site'] = array();
    $ret['site']['url'] = z_root();
    $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), $e['channel_prvkey']));
    $dirmode = get_config('system', 'directory_mode');
    if ($dirmode === false || $dirmode == DIRECTORY_MODE_NORMAL) {
        $ret['site']['directory_mode'] = 'normal';
    }
    if ($dirmode == DIRECTORY_MODE_PRIMARY) {
        $ret['site']['directory_mode'] = 'primary';
    } elseif ($dirmode == DIRECTORY_MODE_SECONDARY) {
        $ret['site']['directory_mode'] = 'secondary';
    } elseif ($dirmode == DIRECTORY_MODE_STANDALONE) {
        $ret['site']['directory_mode'] = 'standalone';
    }
    if ($dirmode != DIRECTORY_MODE_NORMAL) {
        $ret['site']['directory_url'] = z_root() . '/dirsearch';
    }
    // hide detailed site information if you're off the grid
    if ($dirmode != DIRECTORY_MODE_STANDALONE) {
        $register_policy = intval(get_config('system', 'register_policy'));
        if ($register_policy == REGISTER_CLOSED) {
            $ret['site']['register_policy'] = 'closed';
        }
        if ($register_policy == REGISTER_APPROVE) {
            $ret['site']['register_policy'] = 'approve';
        }
        if ($register_policy == REGISTER_OPEN) {
            $ret['site']['register_policy'] = 'open';
        }
        $access_policy = intval(get_config('system', 'access_policy'));
        if ($access_policy == ACCESS_PRIVATE) {
            $ret['site']['access_policy'] = 'private';
        }
        if ($access_policy == ACCESS_PAID) {
            $ret['site']['access_policy'] = 'paid';
        }
        if ($access_policy == ACCESS_FREE) {
            $ret['site']['access_policy'] = 'free';
        }
        if ($access_policy == ACCESS_TIERED) {
            $ret['site']['access_policy'] = 'tiered';
        }
        $ret['site']['accounts'] = account_total();
        require_once 'include/identity.php';
        $ret['site']['channels'] = channel_total();
        $ret['site']['version'] = PLATFORM_NAME . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']';
        $ret['site']['admin'] = get_config('system', 'admin_email');
        $visible_plugins = array();
        if (is_array($a->plugins) && count($a->plugins)) {
            $r = q("select * from addon where hidden = 0");
            if ($r) {
                foreach ($r as $rr) {
                    $visible_plugins[] = $rr['name'];
                }
            }
        }
        $ret['site']['plugins'] = $visible_plugins;
        $ret['site']['sitehash'] = get_config('system', 'location_hash');
        $ret['site']['sitename'] = get_config('system', 'sitename');
        $ret['site']['sellpage'] = get_config('system', 'sellpage');
        $ret['site']['location'] = get_config('system', 'site_location');
        $ret['site']['realm'] = get_directory_realm();
    }
    call_hooks('zot_finger', $ret);
    json_return_and_die($ret);
}
Exemple #7
0
/**
 * @brief Checks the directory mode of this hub.
 *
 * Checks the directory mode of this hub to see if it is some form of directory server. If it is,
 * get the directory realm of this hub. Fetch a list of all other directory servers in this realm and request
 * a directory sync packet. This will contain both directory updates and new ratings. Store these all in the DB. 
 * In the case of updates, we will query each of them asynchronously from a poller task. Ratings are stored 
 * directly if the rater's signature matches.
 *
 * @param int $dirmode;
 */
function sync_directories($dirmode)
{
    if ($dirmode == DIRECTORY_MODE_STANDALONE || $dirmode == DIRECTORY_MODE_NORMAL) {
        return;
    }
    $realm = get_directory_realm();
    if ($realm == DIRECTORY_REALM) {
        $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_type = %d and ( site_realm = '%s' or site_realm = '') ", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()), intval(SITE_TYPE_ZOT), dbesc($realm));
    } else {
        $r = q("select * from site where (site_flags & %d) > 0 and site_url != '%s' and site_realm like '%s' and site_type = %d ", intval(DIRECTORY_MODE_PRIMARY | DIRECTORY_MODE_SECONDARY), dbesc(z_root()), dbesc(protect_sprintf('%' . $realm . '%')), intval(SITE_TYPE_ZOT));
    }
    // If there are no directory servers, setup the fallback master
    /** @FIXME What to do if we're in a different realm? */
    if (!$r && z_root() != DIRECTORY_FALLBACK_MASTER) {
        $r = array();
        $r[] = array('site_url' => DIRECTORY_FALLBACK_MASTER, 'site_flags' => DIRECTORY_MODE_PRIMARY, 'site_update' => NULL_DATE, 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch', 'site_realm' => DIRECTORY_REALM, 'site_valid' => 1);
        $x = q("insert into site ( site_url, site_flags, site_update, site_directory, site_realm, site_valid )\n\t\t\tvalues ( '%s', %d, '%s', '%s', '%s', %d ) ", dbesc($r[0]['site_url']), intval($r[0]['site_flags']), dbesc($r[0]['site_update']), dbesc($r[0]['site_directory']), dbesc($r[0]['site_realm']), intval($r[0]['site_valid']));
        $r = q("select * from site where site_flags in (%d, %d) and site_url != '%s' and site_type = %d ", intval(DIRECTORY_MODE_PRIMARY), intval(DIRECTORY_MODE_SECONDARY), dbesc(z_root()), intval(SITE_TYPE_ZOT));
    }
    if (!$r) {
        return;
    }
    foreach ($r as $rr) {
        if (!$rr['site_directory']) {
            continue;
        }
        logger('sync directories: ' . $rr['site_directory']);
        // for brand new directory servers, only load the last couple of days.
        // It will take about a month for a new directory to obtain the full current repertoire of channels.
        /** @FIXME Go back and pick up earlier ratings if this is a new directory server. These do not get refreshed. */
        $token = get_config('system', 'realm_token');
        $syncdate = $rr['site_sync'] === NULL_DATE ? datetime_convert('UTC', 'UTC', 'now - 2 days') : $rr['site_sync'];
        $x = z_fetch_url($rr['site_directory'] . '?f=&sync=' . urlencode($syncdate) . ($token ? '&t=' . $token : ''));
        if (!$x['success']) {
            continue;
        }
        $j = json_decode($x['body'], true);
        if (!$j['transactions'] || $j['ratings']) {
            continue;
        }
        q("update site set site_sync = '%s' where site_url = '%s'", dbesc(datetime_convert()), dbesc($rr['site_url']));
        logger('sync_directories: ' . $rr['site_url'] . ': ' . print_r($j, true), LOGGER_DATA);
        if (is_array($j['transactions']) && count($j['transactions'])) {
            foreach ($j['transactions'] as $t) {
                $r = q("select * from updates where ud_guid = '%s' limit 1", dbesc($t['transaction_id']));
                if ($r) {
                    continue;
                }
                $ud_flags = 0;
                if (is_array($t['flags']) && in_array('deleted', $t['flags'])) {
                    $ud_flags |= UPDATE_FLAGS_DELETED;
                }
                if (is_array($t['flags']) && in_array('forced', $t['flags'])) {
                    $ud_flags |= UPDATE_FLAGS_FORCED;
                }
                $z = q("insert into updates ( ud_hash, ud_guid, ud_date, ud_flags, ud_addr )\n\t\t\t\t\tvalues ( '%s', '%s', '%s', %d, '%s' ) ", dbesc($t['hash']), dbesc($t['transaction_id']), dbesc($t['timestamp']), intval($ud_flags), dbesc($t['address']));
            }
        }
        if (is_array($j['ratings']) && count($j['ratings'])) {
            foreach ($j['ratings'] as $rr) {
                $x = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' and xlink_static = 1", dbesc($rr['channel']), dbesc($rr['target']));
                if ($x && $x[0]['xlink_updated'] >= $rr['edited']) {
                    continue;
                }
                // Ratings are signed by the rater. We need to verify before we can accept it.
                /** @TODO Queue or defer if the xchan is not yet present on our site */
                $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($rr['channel']));
                if (!$y) {
                    logger('key unavailable on this site for ' . $rr['channel']);
                    continue;
                }
                if (!rsa_verify($rr['target'] . '.' . $rr['rating'] . '.' . $rr['rating_text'], base64url_decode($rr['signature']), $y[0]['xchan_pubkey'])) {
                    logger('failed to verify rating');
                    continue;
                }
                if ($x) {
                    $z = q("update xlink set xlink_rating = %d, xlink_rating_text = '%s', xlink_sig = '%s', xlink_updated = '%s' where xlink_id = %d", intval($rr['rating']), dbesc($rr['rating_text']), dbesc($rr['signature']), dbesc(datetime_convert()), intval($x[0]['xlink_id']));
                    logger('rating updated');
                } else {
                    $z = q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_rating_text, xlink_sig, xlink_updated, xlink_static ) values( '%s', '%s', %d, '%s', '%s', '%s', 1 ) ", dbesc($rr['channel']), dbesc($rr['target']), intval($rr['rating']), dbesc($rr['rating_text']), dbesc($rr['signature']), dbesc(datetime_convert()));
                    logger('rating created');
                }
            }
        }
    }
}
Exemple #8
0
function diaspora_verify_fields($fields, $sig, $pubkey)
{
    if (!$fields) {
        return false;
    }
    $n = array();
    foreach ($fields as $k => $v) {
        if ($k !== 'author_signature' && $k !== 'parent_author_signature') {
            $n[$k] = $v;
        }
    }
    $s = implode($n, ';');
    logger('signing_string: ' . $s);
    return rsa_verify($s, base64_decode($sig), $pubkey);
}
function diaspora_signed_retraction($importer, $xml, $msg)
{
    // obsolete - see https://github.com/SuperTux88/diaspora_federation/issues/27
    $guid = notags(diaspora_get_target_guid($xml));
    $diaspora_handle = notags(diaspora_get_author($xml));
    $type = notags(diaspora_get_type($xml));
    $sig = notags(unxmlify($xml['target_author_signature']));
    $parent_author_signature = $xml['parent_author_signature'] ? notags(unxmlify($xml['parent_author_signature'])) : '';
    $contact = diaspora_get_contact_by_handle($importer['channel_id'], $diaspora_handle);
    if (!$contact) {
        logger('diaspora_signed_retraction: no contact ' . $diaspora_handle . ' for ' . $importer['channel_id']);
        return;
    }
    $signed_data = $guid . ';' . $type;
    $key = $msg['key'];
    /* How Diaspora performs relayable_retraction signature checking:
    
    	   - If an item has been sent by the item author to the top-level post owner to relay on
    	     to the rest of the contacts on the top-level post, the top-level post owner checks
    	     the author_signature, then creates a parent_author_signature before relaying the item on
    	   - If an item has been relayed on by the top-level post owner, the contacts who receive it
    	     check only the parent_author_signature. Basically, they trust that the top-level post
    	     owner has already verified the authenticity of anything he/she sends out
    	   - In either case, the signature that get checked is the signature created by the person
    	     who sent the salmon
    	*/
    if ($parent_author_signature) {
        $parent_author_signature = base64_decode($parent_author_signature);
        if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256')) {
            logger('diaspora_signed_retraction: top-level post owner verification failed');
            return;
        }
    } else {
        $sig_decode = base64_decode($sig);
        if (!rsa_verify($signed_data, $sig_decode, $key, 'sha256')) {
            logger('diaspora_signed_retraction: retraction owner verification failed.' . print_r($msg, true));
            return;
        }
    }
    if ($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
        $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($guid), intval($importer['channel_id']));
        if ($r) {
            if ($r[0]['author_xchan'] == $contact['xchan_hash']) {
                drop_item($r[0]['id'], false, DROPITEM_PHASE1);
                // Now check if the retraction needs to be relayed by us
                //
                // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
                // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
                // The only item with `parent` and `id` as the parent id is the parent item.
                $p = q("select item_flags from item where parent = %d and id = %d limit 1", $r[0]['parent'], $r[0]['parent']);
                if ($p) {
                    if (intval($p[0]['item_origin']) && !$parent_author_signature) {
                        // the existence of parent_author_signature would have meant the parent_author or owner
                        // is already relaying.
                        logger('diaspora_signed_retraction: relaying relayable_retraction');
                        Zotlabs\Daemon\Master::Summon(array('Notifier', 'drop', $r[0]['id']));
                    }
                }
            }
        }
    } else {
        logger('diaspora_signed_retraction: unknown type: ' . $type);
    }
    return 202;
    // NOTREACHED
}
Exemple #10
0
/**
 * @function post_post(&$a)
 *     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)
 *
 * {
 *  "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..."
 * }
 * 
 * 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
 * 
 * {
 *  "type":"pickup",
 *  "url":"http:\/\/example.com",
 *  "callback":"http:\/\/example.com\/post",
 *  "callback_sig":"teE1_fLI...",
 *  "secret":"1eaa...",
 *  "secret_sig":"O7nB4_..."
 * }
 *
 * 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.
 * 
 * 
 * {
 * "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":"*****@*****.**",
 *     "message_top":"*****@*****.**",
 *     "message_parent":"*****@*****.**",
 *     "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":"*****@*****.**",
 *       "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":"*****@*****.**",
 *       "url":"http:\/\/podunk.edu",
 *       "photo":{
 *         "mimetype":"image\/jpeg",
 *         "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5"
 *       },
 *       "guid":"kgVFf_...",
 *       "guid_sig":"PT9-TAp..."
 *     }
 *   }
 * }
 *} 
 *
 * Currently defined message types are 'activity', 'mail', 'profile' 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.
 * 
 * {
 *  "type": "ping"
 * }
 * 
 * On receipt of a ping packet a ping response will be returned:
 *
 * {
 *   "success" : 1,
 *   "site" {
 *       "url":"http:\/\/podunk.edu",
 *       "url_sig":"T8Bp7j5...",
 *       "sitekey": "-----BEGIN PUBLIC KEY-----
 *                  MIICIjANBgkqhkiG9w0BAQE..."
 *    }
 * }
 * 
 * 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.  
 *
 */
function post_post(&$a)
{
    $encrypted_packet = false;
    $ret = array('success' => false);
    $data = json_decode($_REQUEST['data'], true);
    /**
     * Many message packets will arrive encrypted. The existence of an 'iv' element 
     * tells us we need to unencapsulate the AES-256-CBC content using the site private key
     */
    if (array_key_exists('iv', $data)) {
        $encrypted_packet = true;
        $data = crypto_unencapsulate($data, get_config('system', 'prvkey'));
        logger('mod_zot: decrypt1: ' . $data, LOGGER_DATA);
        $data = json_decode($data, true);
    }
    if (!$data) {
        // possible Bleichenbacher's attack, just treat it as a
        // message we have no handler for. It should fail a bit
        // further along with "no hub". Our public key is public
        // knowledge. There's no reason why anybody should get the
        // encryption wrong unless they're fishing or hacking. If
        // they're developing and made a goof, this can be discovered
        // in the logs of the destination site. If they're fishing or
        // hacking, the bottom line is we can't verify their hub.
        // That's all we're going to tell them.
        $data = array('type' => 'bogus');
    }
    $msgtype = array_key_exists('type', $data) ? $data['type'] : '';
    if ($msgtype === 'ping') {
        // Useful to get a health check on a remote site.
        // This will let us know if any important communication details
        // that we may have stored are no longer valid, regardless of xchan details.
        logger('POST: got ping send pong now back: ' . z_root(), LOGGER_DEBUG);
        $ret['success'] = true;
        $ret['site'] = array();
        $ret['site']['url'] = z_root();
        $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(), get_config('system', 'prvkey')));
        $ret['site']['sitekey'] = get_config('system', 'pubkey');
        json_return_and_die($ret);
    }
    if ($msgtype === 'pickup') {
        /**
         * The 'pickup' message arrives with a tracking ID which is associated with a particular outq_hash
         * First verify that that the returned signatures verify, then check that we have an outbound queue item
         * with the correct hash.
         * If everything verifies, find any/all outbound messages in the queue for this hubloc and send them back
         *
         */
        if (!$data['secret'] || !$data['secret_sig']) {
            $ret['message'] = 'no verification signature';
            logger('mod_zot: pickup: ' . $ret['message'], LOGGER_DEBUG);
            json_return_and_die($ret);
        }
        $r = q("select distinct hubloc_sitekey from hubloc where hubloc_url = '%s' and hubloc_callback = '%s' and hubloc_sitekey != '' group by hubloc_sitekey ", dbesc($data['url']), dbesc($data['callback']));
        if (!$r) {
            $ret['message'] = 'site not found';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        foreach ($r as $hubsite) {
            // verify the url_sig
            // If the server was re-installed at some point, there could be multiple hubs with the same url and callback.
            // Only one will have a valid key.
            $forgery = true;
            $secret_fail = true;
            $sitekey = $hubsite['hubloc_sitekey'];
            logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA);
            if (rsa_verify($data['callback'], base64url_decode($data['callback_sig']), $sitekey)) {
                $forgery = false;
            }
            if (rsa_verify($data['secret'], base64url_decode($data['secret_sig']), $sitekey)) {
                $secret_fail = false;
            }
            if (!$forgery && !$secret_fail) {
                break;
            }
        }
        if ($forgery) {
            $ret['message'] = 'possible site forgery';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        if ($secret_fail) {
            $ret['message'] = 'secret validation failed';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        /**
         * If we made it to here, the signatures verify, but we still don't know if the tracking ID is valid.
         * It wouldn't be an error if the tracking ID isn't found, because we may have sent this particular
         * queue item with another pickup (after the tracking ID for the other pickup  was verified). 
         */
        $r = q("select outq_posturl from outq where outq_hash = '%s' and outq_posturl = '%s' limit 1", dbesc($data['secret']), dbesc($data['callback']));
        if (!$r) {
            $ret['message'] = 'nothing to pick up';
            logger('mod_zot: pickup: ' . $ret['message']);
            json_return_and_die($ret);
        }
        /**
         * Everything is good if we made it here, so find all messages that are going to this location
         * and send them all.
         */
        $r = q("select * from outq where outq_posturl = '%s'", dbesc($data['callback']));
        if ($r) {
            logger('mod_zot: succesful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG);
            $ret['success'] = true;
            $ret['pickup'] = array();
            foreach ($r as $rr) {
                $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'], true), 'message' => json_decode($rr['outq_msg'], true));
                $x = q("delete from outq where outq_hash = '%s' limit 1", dbesc($rr['outq_hash']));
            }
        }
        $encrypted = crypto_encapsulate(json_encode($ret), $sitekey);
        json_return_and_die($encrypted);
        /** pickup: end */
    }
    /**
     * All other message types require us to verify the sender. This is a generic check, so we 
     * will do it once here and bail if anything goes wrong.
     */
    if (array_key_exists('sender', $data)) {
        $sender = $data['sender'];
    }
    /** Check if the sender is already verified here */
    $hub = zot_gethub($sender);
    if (!$hub) {
        /** 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($sender);
        if (!$result['success'] || !($hub = zot_gethub($sender))) {
            $ret['message'] = 'Hub not available.';
            logger('mod_zot: no hub');
            json_return_and_die($ret);
        }
    }
    // Update our DB to show when we last communicated successfully with this hub
    // This will allow us to prune dead hubs from using up resources
    $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d limit 1", dbesc(datetime_convert()), intval($hub['hubloc_id']));
    // a dead hub came back to life - reset any tombstones we might have
    if ($hub['hubloc_status'] & HUBLOC_OFFLINE) {
        q("update hubloc set hubloc_status = (hubloc_status ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_OFFLINE), intval($hub['hubloc_id']));
        if ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_ORPHANCHECK) {
            q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_ORPHANCHECK), intval($hub['hubloc_id']));
        }
        q("update xchan set xchan_flags = (xchan_flags ^ %d) where (xchan_flags & %d) and xchan_hash = '%s' limit 1", intval(XCHAN_FLAGS_ORPHAN), intval(XCHAN_FLAGS_ORPHAN), dbesc($hub['hubloc_hash']));
    }
    /** 
     * This hub has now been proven to be valid.
     * Any hub with the same URL and a different sitekey cannot be valid.
     * Get rid of them (mark them deleted). There's a good chance they were re-installs.
     *
     */
    q("update hubloc set hubloc_flags = ( hubloc_flags | %d ) where hubloc_url = '%s' and hubloc_sitekey != '%s' ", intval(HUBLOC_FLAGS_DELETED), dbesc($hub['hubloc_url']), dbesc($hub['hubloc_sitekey']));
    // TODO: check which hub is primary and take action if mismatched
    if (array_key_exists('recipients', $data)) {
        $recipients = $data['recipients'];
    }
    if ($msgtype === 'auth_check') {
        /**
         * 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
        q("delete from verify where type = 'auth' and created < UTC_TIMESTAMP() - INTERVAL 10 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 = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", intval($c[0]['channel_id']), dbesc($data['secret']), dbesc($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);
            }
            $r = q("delete from verify where id = %d limit 1", intval($z[0]['id']));
            $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
            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;
            }
            json_return_and_die($ret);
        }
        json_return_and_die($ret);
    }
    if ($msgtype === 'purge') {
        if ($recipients) {
            // basically this means "unfriend"
            foreach ($recipients as $recip) {
                $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
                if ($r) {
                    $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", intval($r[0]['channel_id']), dbesc(make_xchan_hash($sender['guid'], $sender['guid_sig'])));
                    if ($r) {
                        contact_remove($r[0]['channel_id'], $r[0]['abook_id']);
                    }
                }
            }
        } else {
            // Unfriend everybody - basically this means the channel has committed suicide
            $arr = $data['sender'];
            $sender_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']);
            require_once 'include/Contact.php';
            remove_all_xchan_resources($sender_hash);
            $ret['success'] = true;
            json_return_and_die($ret);
        }
    }
    if ($msgtype === 'refresh' || $msgtype === 'force_refresh') {
        // remote channel info (such as permissions or photo or something)
        // has been updated. Grab a fresh copy and sync it.
        // The difference between refresh and force_refresh is that
        // force_refresh unconditionally creates a directory update record,
        // even if no changes were detected upon processing.
        if ($recipients) {
            // This would be a permissions update, typically for one connection
            foreach ($recipients as $recip) {
                $r = q("select channel.*,xchan.* from channel \n\t\t\t\t\tleft join xchan on channel_hash = xchan_hash\n\t\t\t\t\twhere channel_guid = '%s' and channel_guid_sig = '%s' limit 1", dbesc($recip['guid']), dbesc($recip['guid_sig']));
                $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), $r[0], $msgtype === 'force_refresh' ? true : false);
            }
        } else {
            // system wide refresh
            $x = zot_refresh(array('xchan_guid' => $sender['guid'], 'xchan_guid_sig' => $sender['guid_sig'], 'hubloc_url' => $sender['url']), null, $msgtype === 'force_refresh' ? true : false);
        }
        $ret['success'] = true;
        json_return_and_die($ret);
    }
    if ($msgtype === 'notify') {
        $async = get_config('system', 'queued_fetch');
        if ($async) {
            // add to receive queue
            // qreceive_add($data);
        } else {
            $x = zot_fetch($data);
            $ret['delivery_report'] = $x;
        }
        $ret['success'] = true;
        json_return_and_die($ret);
    }
    // catchall
    json_return_and_die($ret);
}
Exemple #11
0
 private function getAllinpyOrderResult()
 {
     require_once C('APP_ROOT') . "Lib/Pay/allinpay/php_rsa.php";
     $result = array();
     $merchantId = $_REQUEST["merchantId"];
     $version = $_REQUEST['version'];
     $language = $_REQUEST['language'];
     $signType = $_REQUEST['signType'];
     $payType = $_REQUEST['payType'];
     $issuerId = $_REQUEST['issuerId'];
     $paymentOrderId = $_REQUEST['paymentOrderId'];
     $orderNo = $_REQUEST['orderNo'];
     $orderDatetime = $_REQUEST['orderDatetime'];
     $orderAmount = $_REQUEST['orderAmount'];
     $payDatetime = $_REQUEST['payDatetime'];
     $payAmount = $_REQUEST['payAmount'];
     $ext1 = $_REQUEST['ext1'];
     $ext2 = $_REQUEST['ext2'];
     $payResult = $_REQUEST['payResult'];
     $errorCode = $_REQUEST['errorCode'];
     $returnDatetime = $_REQUEST['returnDatetime'];
     $signMsg = $_REQUEST["signMsg"];
     $bufSignSrc = "";
     if ($merchantId != "") {
         $bufSignSrc = $bufSignSrc . "merchantId=" . $merchantId . "&";
     }
     if ($version != "") {
         $bufSignSrc = $bufSignSrc . "version=" . $version . "&";
     }
     if ($language != "") {
         $bufSignSrc = $bufSignSrc . "language=" . $language . "&";
     }
     if ($signType != "") {
         $bufSignSrc = $bufSignSrc . "signType=" . $signType . "&";
     }
     if ($payType != "") {
         $bufSignSrc = $bufSignSrc . "payType=" . $payType . "&";
     }
     if ($issuerId != "") {
         $bufSignSrc = $bufSignSrc . "issuerId=" . $issuerId . "&";
     }
     if ($paymentOrderId != "") {
         $bufSignSrc = $bufSignSrc . "paymentOrderId=" . $paymentOrderId . "&";
     }
     if ($orderNo != "") {
         $bufSignSrc = $bufSignSrc . "orderNo=" . $orderNo . "&";
     }
     if ($orderDatetime != "") {
         $bufSignSrc = $bufSignSrc . "orderDatetime=" . $orderDatetime . "&";
     }
     if ($orderAmount != "") {
         $bufSignSrc = $bufSignSrc . "orderAmount=" . $orderAmount . "&";
     }
     if ($payDatetime != "") {
         $bufSignSrc = $bufSignSrc . "payDatetime=" . $payDatetime . "&";
     }
     if ($payAmount != "") {
         $bufSignSrc = $bufSignSrc . "payAmount=" . $payAmount . "&";
     }
     if ($ext1 != "") {
         $bufSignSrc = $bufSignSrc . "ext1=" . $ext1 . "&";
     }
     if ($ext2 != "") {
         $bufSignSrc = $bufSignSrc . "ext2=" . $ext2 . "&";
     }
     if ($payResult != "") {
         $bufSignSrc = $bufSignSrc . "payResult=" . $payResult . "&";
     }
     if ($errorCode != "") {
         $bufSignSrc = $bufSignSrc . "errorCode=" . $errorCode . "&";
     }
     if ($returnDatetime != "") {
         $bufSignSrc = $bufSignSrc . "returnDatetime=" . $returnDatetime;
     }
     $allinpay_params = C('ALLINPAY_PARAMS');
     //验签
     //解析publickey.txt文本获取公钥信息
     $publickeyfile = C('APP_ROOT') . $allinpay_params[$allinpay_params["MODE"] . '_KEY'];
     $publickeycontent = file_get_contents($publickeyfile);
     //echo "<br>".$content;
     $publickeyarray = explode(PHP_EOL, $publickeycontent);
     $publickey = explode('=', $publickeyarray[0]);
     $modulus = explode('=', $publickeyarray[1]);
     //echo "<br>publickey=".$publickey[1];
     //echo "<br>modulus=".$modulus[1];
     $keylength = 1024;
     //验签结果
     $verify_result = rsa_verify($bufSignSrc, $signMsg, $publickey[1], $modulus[1], $keylength, "sha1");
     $result['verify_result'] = $verify_result;
     $result['merchantId'] = $merchantId;
     $result['version'] = $version;
     $result['language'] = $language;
     $result['signType'] = $signType;
     $result['payType'] = $payType;
     $result['issuerId'] = $issuerId;
     $result['paymentOrderId'] = $paymentOrderId;
     $result['orderNo'] = $orderNo;
     $result['orderDatetime'] = $orderDatetime;
     $result['orderAmount'] = $orderAmount;
     $result['payDatetime'] = $payDatetime;
     $result['payAmount'] = $payAmount;
     $result['ext1'] = $ext1;
     $result['ext2'] = $ext2;
     $result['payResult'] = $payResult;
     $result['errorCode'] = $errorCode;
     $result['returnDatetime'] = $returnDatetime;
     return $result;
 }
Exemple #12
0
$params = $_POST;
ksort($params);
$str = "";
foreach ($params as $key => $value) {
    if ($key == "sign") {
        continue;
    }
    if (strlen($str) == 0) {
        $str = $key . "=" . stripslashes($value);
    } else {
        $str = $str . "&" . $key . "=" . stripslashes($value);
    }
}
$sign = base64_decode(stripslashes($_POST["sign"]));
$pubkey = "-----BEGIN PUBLIC KEY-----\r\n" . chunk_split($pf_info["PubKey"], 64, "\r\n") . "-----END PUBLIC KEY-----";
if (!rsa_verify($str, $sign, $pubkey)) {
    log::instance()->error("ret: 签名无效");
    echo "{\"result\":1}";
    exit;
}
// end verify
if (stripslashes($_POST["result"]) != '0') {
    log::instance()->error("ret: 支付失败");
    echo "{\"result\":0}";
    exit;
}
$note = json_decode(stripslashes($_POST["extReserved"]), true);
$ret = recharge($pf_info["PF"], $note["sid"], stripslashes($_POST["requestId"]), $note["uid"], $note["item"], stripslashes($_POST["amount"]), stripslashes($_POST["orderId"]), 0);
log::instance()->debug("ret: " . $ret);
if ($ret == "SUCCESS" || $ret == "TRADE_NO NOT EXIST") {
    echo "{\"result\":0}";
Exemple #13
0
 public function notify($request)
 {
     $return_res = array('info' => '', 'status' => false);
     //file_put_contents("./system/payment/log/notify_".strftime("%Y%m%d%H%M%S",time()).".txt",print_r($request,true));
     //$payment_id = $GLOBALS['db']->getOne("select payment_id from ".DB_PREFIX."payment_log where id=".intval($ext1));
     $payment = $GLOBALS['db']->getRow("select id,config from " . DB_PREFIX . "payment where class_name='Allinpay'");
     $payment['config'] = unserialize($payment['config']);
     //print_r($payment['config']);exit;
     $merchant_acctid = trim($payment['config']['merchant_id']);
     //人民币账号 不可空
     $key = trim($payment['config']['md5_key']);
     $merchantId = $request["merchantId"];
     $version = $request['version'];
     $language = $request['language'];
     $signType = $request['signType'];
     $payType = $request['payType'];
     $issuerId = $request['issuerId'];
     $paymentOrderId = $request['paymentOrderId'];
     $orderNo = $request['orderNo'];
     $orderDatetime = $request['orderDatetime'];
     $orderAmount = $request['orderAmount'];
     $payDatetime = $request['payDatetime'];
     $payAmount = $request['payAmount'];
     $ext1 = $request['ext1'];
     $ext2 = $request['ext2'];
     $payResult = $request['payResult'];
     $errorCode = $request['errorCode'];
     $returnDatetime = $request['returnDatetime'];
     $signMsg = $request["signMsg"];
     $bufSignSrc = "";
     if ($merchantId != "") {
         $bufSignSrc = $bufSignSrc . "merchantId=" . $merchantId . "&";
     }
     if ($version != "") {
         $bufSignSrc = $bufSignSrc . "version=" . $version . "&";
     }
     if ($language != "") {
         $bufSignSrc = $bufSignSrc . "language=" . $language . "&";
     }
     if ($signType != "") {
         $bufSignSrc = $bufSignSrc . "signType=" . $signType . "&";
     }
     if ($payType != "") {
         $bufSignSrc = $bufSignSrc . "payType=" . $payType . "&";
     }
     if ($issuerId != "") {
         $bufSignSrc = $bufSignSrc . "issuerId=" . $issuerId . "&";
     }
     if ($paymentOrderId != "") {
         $bufSignSrc = $bufSignSrc . "paymentOrderId=" . $paymentOrderId . "&";
     }
     if ($orderNo != "") {
         $bufSignSrc = $bufSignSrc . "orderNo=" . $orderNo . "&";
     }
     if ($orderDatetime != "") {
         $bufSignSrc = $bufSignSrc . "orderDatetime=" . $orderDatetime . "&";
     }
     if ($orderAmount != "") {
         $bufSignSrc = $bufSignSrc . "orderAmount=" . $orderAmount . "&";
     }
     if ($payDatetime != "") {
         $bufSignSrc = $bufSignSrc . "payDatetime=" . $payDatetime . "&";
     }
     if ($payAmount != "") {
         $bufSignSrc = $bufSignSrc . "payAmount=" . $payAmount . "&";
     }
     if ($ext1 != "") {
         $bufSignSrc = $bufSignSrc . "ext1=" . $ext1 . "&";
     }
     if ($ext2 != "") {
         $bufSignSrc = $bufSignSrc . "ext2=" . $ext2 . "&";
     }
     if ($payResult != "") {
         $bufSignSrc = $bufSignSrc . "payResult=" . $payResult . "&";
     }
     if ($errorCode != "") {
         $bufSignSrc = $bufSignSrc . "errorCode=" . $errorCode . "&";
     }
     if ($returnDatetime != "") {
         $bufSignSrc = $bufSignSrc . "returnDatetime=" . $returnDatetime;
     }
     /*
     //验签
     //解析publickey.txt文本获取公钥信息
     $publickeycontent = trim($payment['config']['public_key']);
     //echo "<br>".$content;
     $publickeyarray = explode(PHP_EOL, $publickeycontent);
     $publickey = explode('=',$publickeyarray[0]);
     $modulus = explode('=',$publickeyarray[1]);
     //echo "<br>publickey=".$publickey[1];
     //echo "<br>modulus=".$modulus[1];
     */
     $publickey = trim($payment['config']['public_exponent']);
     $modulus = trim($payment['config']['public_modulus']);
     require_once APP_ROOT_PATH . "system/payment/Allinpay/php_rsa.php";
     $keylength = 1024;
     //验签结果
     //$verifyResult = rsa_verify($bufSignSrc,$signMsg, $publickey[1], $modulus[1], $keylength,"sha1");
     $verifyResult = rsa_verify($bufSignSrc, $signMsg, $publickey, $modulus, $keylength, "sha1");
     /*
     echo 'bufSignSrc:'.$bufSignSrc."<br>";
     	echo 'signMsg:'.$signMsg."<br>";
     	echo 'publickey:'.$publickey."<br>";
     	echo 'modulus:'.$modulus."<br>";
     	
     	if($verifyResult){	
     echo "报文验签成功!";
     	}else{
     echo "报文验签失败!";
     	}
     	exit;
     */
     if ($verifyResult) {
         $payment_notice = $GLOBALS['db']->getRow("select * from " . DB_PREFIX . "payment_notice where notice_sn = '" . $orderNo . "'");
         require_once APP_ROOT_PATH . "system/libs/cart.php";
         $rs = payment_paid($payment_notice['id'], $paymentOrderId);
         $is_paid = intval($GLOBALS['db']->getOne("select is_paid from " . DB_PREFIX . "payment_notice where id = '" . intval($payment_notice['id']) . "'"));
         if ($is_paid == 1) {
             echo '1';
         } else {
             echo '0';
         }
     } else {
         echo '0';
     }
 }
Exemple #14
0
 /**
  * @brief Look up information about channel.
  *
  * @param string $webbie
  *   does not have to be host qualified e.g. 'foo' is treated as 'foo\@thishub'
  * @param array $channel
  *   (optional), if supplied permissions will be enumerated specifically for $channel
  * @param boolean $autofallback
  *   fallback/failover to http if https connection cannot be established. Default is true.
  *
  * @return zotinfo array (with 'success' => true) or array('success' => false);
  */
 public static function run($webbie, $channel = null, $autofallback = true)
 {
     $ret = array('success' => false);
     self::$token = random_string();
     if (strpos($webbie, '@') === false) {
         $address = $webbie;
         $host = App::get_hostname();
     } else {
         $address = substr($webbie, 0, strpos($webbie, '@'));
         $host = substr($webbie, strpos($webbie, '@') + 1);
     }
     $xchan_addr = $address . '@' . $host;
     if (!$address || !$xchan_addr) {
         logger('zot_finger: no address :' . $webbie);
         return $ret;
     }
     logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA, LOG_DEBUG);
     // potential issue here; the xchan_addr points to the primary hub.
     // The webbie we were called with may not, so it might not be found
     // unless we query for hubloc_addr instead of xchan_addr
     $r = q("select xchan.*, hubloc.* from xchan\n\t\t\tleft join hubloc on xchan_hash = hubloc_hash\n\t\t\twhere xchan_addr = '%s' and hubloc_primary = 1 limit 1", dbesc($xchan_addr));
     if ($r) {
         $url = $r[0]['hubloc_url'];
         if ($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') {
             logger('zot_finger: alternate network: ' . $webbie);
             logger('url: ' . $url . ', net: ' . var_export($r[0]['hubloc_network'], true), LOGGER_DATA, LOG_DEBUG);
             return $ret;
         }
     } else {
         $url = 'https://' . $host;
     }
     $rhs = '/.well-known/zot-info';
     $https = strpos($url, 'https://') === 0 ? true : false;
     logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG);
     if ($channel) {
         $postvars = array('address' => $address, 'target' => $channel['channel_guid'], 'target_sig' => $channel['channel_guid_sig'], 'key' => $channel['channel_pubkey'], 'token' => self::$token);
         $result = z_post_url($url . $rhs, $postvars);
         if (!$result['success'] && $autofallback) {
             if ($https) {
                 logger('zot_finger: https failed. falling back to http');
                 $result = z_post_url('http://' . $host . $rhs, $postvars);
             }
         }
     } else {
         $rhs .= '?f=&address=' . urlencode($address) . '&token=' . self::$token;
         $result = z_fetch_url($url . $rhs);
         if (!$result['success'] && $autofallback) {
             if ($https) {
                 logger('zot_finger: https failed. falling back to http');
                 $result = z_fetch_url('http://' . $host . $rhs);
             }
         }
     }
     if (!$result['success']) {
         logger('zot_finger: no results');
         return $ret;
     }
     $x = json_decode($result['body'], true);
     if ($x) {
         $signed_token = is_array($x) && array_key_exists('signed_token', $x) ? $x['signed_token'] : null;
         if ($signed_token) {
             $valid = rsa_verify('token.' . self::$token, base64url_decode($signed_token), $x['key']);
             if (!$valid) {
                 logger('invalid signed token: ' . $url . $rhs, LOGGER_NORMAL, LOG_ERR);
                 return $ret;
             }
         } else {
             logger('No signed token from ' . $url . $rhs, LOGGER_NORMAL, LOG_WARNING);
             // after 2017-01-01 this will be a hard error unless you over-ride it.
             if (time() > 1483228800 && !get_config('system', 'allow_unsigned_zotfinger')) {
                 return $ret;
             }
         }
     }
     return $x;
 }
Exemple #15
0
function diaspora_signed_retraction($importer, $xml, $msg)
{
    $guid = notags(unxmlify($xml->target_guid));
    $diaspora_handle = notags(unxmlify($xml->sender_handle));
    $type = notags(unxmlify($xml->target_type));
    $sig = notags(unxmlify($xml->target_author_signature));
    $contact = diaspora_get_contact_by_handle($importer['uid'], $diaspora_handle);
    if (!$contact) {
        logger('diaspora_signed_retraction: no contact');
        return;
    }
    // this may not yet work for comments. Need to see how the relaying works
    // and figure out who signs it.
    $signed_data = $guid . ';' . $type;
    $sig = base64_decode($sig);
    $key = $msg['key'];
    if (!rsa_verify($signed_data, $sig, $key, 'sha256')) {
        logger('diaspora_signed_retraction: owner verification failed.' . print_r($msg, true));
        return;
    }
    if ($type === 'StatusMessage') {
        $r = q("select * from item where guid = '%s' and uid = %d limit 1", dbesc($guid), intval($importer['uid']));
        if (count($r)) {
            if (link_compare($r[0]['author-link'], $contact['url'])) {
                q("update item set `deleted` = 1, `changed` = '%s' where `id` = %d limit 1", dbesc(datetime_convert()), intval($r[0]['id']));
            }
        }
    } else {
        logger('diaspora_signed_retraction: unknown type: ' . $type);
    }
    return 202;
    // NOTREACHED
}
Exemple #16
0
function salmon_post(&$a)
{
    $xml = file_get_contents('php://input');
    logger('mod-salmon: new salmon ' . $xml, LOGGER_DATA);
    $nick = $a->argc > 1 ? notags(trim($a->argv[1])) : '';
    $mentions = $a->argc > 2 && $a->argv[2] === 'mention' ? true : false;
    $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1", dbesc($nick));
    if (!count($r)) {
        http_status_exit(500);
    }
    $importer = $r[0];
    // parse the xml
    $dom = simplexml_load_string($xml, 'SimpleXMLElement', 0, NAMESPACE_SALMON_ME);
    // figure out where in the DOM tree our data is hiding
    if ($dom->provenance->data) {
        $base = $dom->provenance;
    } elseif ($dom->env->data) {
        $base = $dom->env;
    } elseif ($dom->data) {
        $base = $dom;
    }
    if (!$base) {
        logger('mod-salmon: unable to locate salmon data in xml ');
        http_status_exit(400);
    }
    // Stash the signature away for now. We have to find their key or it won't be good for anything.
    $signature = base64url_decode($base->sig);
    // unpack the  data
    // strip whitespace so our data element will return to one big base64 blob
    $data = str_replace(array(" ", "\t", "\r", "\n"), array("", "", "", ""), $base->data);
    // stash away some other stuff for later
    $type = $base->data[0]->attributes()->type[0];
    $keyhash = $base->sig[0]->attributes()->keyhash[0];
    $encoding = $base->encoding;
    $alg = $base->alg;
    // Salmon magic signatures have evolved and there is no way of knowing ahead of time which
    // flavour we have. We'll try and verify it regardless.
    $stnet_signed_data = $data;
    $signed_data = $data . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg);
    $compliant_format = str_replace('=', '', $signed_data);
    // decode the data
    $data = base64url_decode($data);
    $author = ostatus_salmon_author($data, $importer);
    $author_link = $author["author-link"];
    if (!$author_link) {
        logger('mod-salmon: Could not retrieve author URI.');
        http_status_exit(400);
    }
    // Once we have the author URI, go to the web and try to find their public key
    logger('mod-salmon: Fetching key for ' . $author_link);
    $key = get_salmon_key($author_link, $keyhash);
    if (!$key) {
        logger('mod-salmon: Could not retrieve author key.');
        http_status_exit(400);
    }
    $key_info = explode('.', $key);
    $m = base64url_decode($key_info[1]);
    $e = base64url_decode($key_info[2]);
    logger('mod-salmon: key details: ' . print_r($key_info, true), LOGGER_DEBUG);
    $pubkey = metopem($m, $e);
    // We should have everything we need now. Let's see if it verifies.
    $verify = rsa_verify($compliant_format, $signature, $pubkey);
    if (!$verify) {
        logger('mod-salmon: message did not verify using protocol. Trying padding hack.');
        $verify = rsa_verify($signed_data, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: message did not verify using padding. Trying old statusnet hack.');
        $verify = rsa_verify($stnet_signed_data, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: Message did not verify. Discarding.');
        http_status_exit(400);
    }
    logger('mod-salmon: Message verified.');
    /*
     *
     * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
     *
     */
    $r = q("SELECT * FROM `contact` WHERE `network` IN ('%s', '%s')\n\t\t\t\t\t\tAND (`nurl` = '%s' OR `alias` = '%s' OR `alias` = '%s')\n\t\t\t\t\t\tAND `uid` = %d LIMIT 1", dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN), dbesc(normalise_link($author_link)), dbesc($author_link), dbesc(normalise_link($author_link)), intval($importer['uid']));
    if (!count($r)) {
        logger('mod-salmon: Author unknown to us.');
        if (get_pconfig($importer['uid'], 'system', 'ostatus_autofriend')) {
            $result = new_contact($importer['uid'], $author_link);
            if ($result['success']) {
                $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s') \n\t\t\t\t\tAND `uid` = %d LIMIT 1", dbesc(NETWORK_OSTATUS), dbesc($author_link), dbesc($author_link), intval($importer['uid']));
            }
        }
    }
    // Have we ignored the person?
    // If so we can not accept this post.
    //if((count($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == CONTACT_IS_FOLLOWER) || ($r[0]['blocked']))) {
    if (count($r) && $r[0]['blocked']) {
        logger('mod-salmon: Ignoring this author.');
        http_status_exit(202);
        // NOTREACHED
    }
    // Placeholder for hub discovery.
    $hub = '';
    $contact_rec = count($r) ? $r[0] : null;
    ostatus_import($data, $importer, $contact_rec, $hub);
    http_status_exit(200);
}
Exemple #17
0
function salmon_post(&$a)
{
    $xml = file_get_contents('php://input');
    logger('mod-salmon: new salmon ' . $xml, LOGGER_DATA);
    $nick = $a->argc > 1 ? notags(trim($a->argv[1])) : '';
    $mentions = $a->argc > 2 && $a->argv[2] === 'mention' ? true : false;
    $r = q("SELECT * FROM `user` WHERE `nickname` = '%s' AND `account_expired` = 0 AND `account_removed` = 0 LIMIT 1", dbesc($nick));
    if (!count($r)) {
        http_status_exit(500);
    }
    $importer = $r[0];
    // parse the xml
    $dom = simplexml_load_string($xml, 'SimpleXMLElement', 0, NAMESPACE_SALMON_ME);
    // figure out where in the DOM tree our data is hiding
    if ($dom->provenance->data) {
        $base = $dom->provenance;
    } elseif ($dom->env->data) {
        $base = $dom->env;
    } elseif ($dom->data) {
        $base = $dom;
    }
    if (!$base) {
        logger('mod-salmon: unable to locate salmon data in xml ');
        http_status_exit(400);
    }
    // Stash the signature away for now. We have to find their key or it won't be good for anything.
    $signature = base64url_decode($base->sig);
    // unpack the  data
    // strip whitespace so our data element will return to one big base64 blob
    $data = str_replace(array(" ", "\t", "\r", "\n"), array("", "", "", ""), $base->data);
    // stash away some other stuff for later
    $type = $base->data[0]->attributes()->type[0];
    $keyhash = $base->sig[0]->attributes()->keyhash[0];
    $encoding = $base->encoding;
    $alg = $base->alg;
    // Salmon magic signatures have evolved and there is no way of knowing ahead of time which
    // flavour we have. We'll try and verify it regardless.
    $stnet_signed_data = $data;
    $signed_data = $data . '.' . base64url_encode($type) . '.' . base64url_encode($encoding) . '.' . base64url_encode($alg);
    $compliant_format = str_replace('=', '', $signed_data);
    // decode the data
    $data = base64url_decode($data);
    // Remove the xml declaration
    $data = preg_replace('/\\<\\?xml[^\\?].*\\?\\>/', '', $data);
    // Create a fake feed wrapper so simplepie doesn't choke
    $tpl = get_markup_template('fake_feed.tpl');
    $base = substr($data, strpos($data, '<entry'));
    $feedxml = $tpl . $base . '</feed>';
    logger('mod-salmon: Processed feed: ' . $feedxml);
    // Now parse it like a normal atom feed to scrape out the author URI
    $feed = new SimplePie();
    $feed->set_raw_data($feedxml);
    $feed->enable_order_by_date(false);
    $feed->init();
    logger('mod-salmon: Feed parsed.');
    if ($feed->get_item_quantity()) {
        foreach ($feed->get_items() as $item) {
            $author = $item->get_author();
            $author_link = unxmlify($author->get_link());
            break;
        }
    }
    if (!$author_link) {
        logger('mod-salmon: Could not retrieve author URI.');
        http_status_exit(400);
    }
    // Once we have the author URI, go to the web and try to find their public key
    logger('mod-salmon: Fetching key for ' . $author_link);
    $key = get_salmon_key($author_link, $keyhash);
    if (!$key) {
        logger('mod-salmon: Could not retrieve author key.');
        http_status_exit(400);
    }
    $key_info = explode('.', $key);
    $m = base64url_decode($key_info[1]);
    $e = base64url_decode($key_info[2]);
    logger('mod-salmon: key details: ' . print_r($key_info, true), LOGGER_DEBUG);
    $pubkey = metopem($m, $e);
    // We should have everything we need now. Let's see if it verifies.
    $verify = rsa_verify($compliant_format, $signature, $pubkey);
    if (!$verify) {
        logger('mod-salmon: message did not verify using protocol. Trying padding hack.');
        $verify = rsa_verify($signed_data, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: message did not verify using padding. Trying old statusnet hack.');
        $verify = rsa_verify($stnet_signed_data, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: Message did not verify. Discarding.');
        http_status_exit(400);
    }
    logger('mod-salmon: Message verified.');
    /*
     *
     * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
     *
     */
    $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s' ) \n\t\tAND `uid` = %d LIMIT 1", dbesc(NETWORK_OSTATUS), dbesc($author_link), dbesc($author_link), intval($importer['uid']));
    if (!count($r)) {
        logger('mod-salmon: Author unknown to us.');
        if (get_pconfig($importer['uid'], 'system', 'ostatus_autofriend')) {
            require_once 'include/follow.php';
            $result = new_contact($importer['uid'], $author_link);
            if ($result['success']) {
                $r = q("SELECT * FROM `contact` WHERE `network` = '%s' AND ( `url` = '%s' OR `alias` = '%s' ) \n\t\t\t\t\tAND `uid` = %d LIMIT 1", dbesc(NETWORK_OSTATUS), dbesc($author_link), dbesc($author_link), intval($importer['uid']));
            }
        }
    }
    // is this a follower? Or have we ignored the person?
    // If so we can not accept this post.
    if (count($r) && ($r[0]['readonly'] || $r[0]['rel'] == CONTACT_IS_FOLLOWER || $r[0]['blocked'])) {
        logger('mod-salmon: Ignoring this author.');
        http_status_exit(202);
        // NOTREACHED
    }
    require_once 'include/items.php';
    // Placeholder for hub discovery. We shouldn't find any hubs
    // since we supplied the fake feed header - and it doesn't have any.
    $hub = '';
    /**
     *
     * anti-spam measure: consume_feed will accept a follow activity from 
     * this person (and nothing else) if there is no existing contact record.
     *
     */
    $contact_rec = count($r) ? $r[0] : null;
    consume_feed($feedxml, $importer, $contact_rec, $hub);
    http_status_exit(200);
}
Exemple #18
0
/**
 * @brief
 *
 * @param array $arr
 * @param string $pubkey
 * @return boolean true if updated or inserted
 */
function import_site($arr, $pubkey)
{
    if (!is_array($arr) || !$arr['url'] || !$arr['url_sig']) {
        return false;
    }
    if (!rsa_verify($arr['url'], base64url_decode($arr['url_sig']), $pubkey)) {
        logger('import_site: bad url_sig');
        return false;
    }
    $update = false;
    $exists = false;
    $r = q("select * from site where site_url = '%s' limit 1", dbesc($arr['url']));
    if ($r) {
        $exists = true;
        $siterecord = $r[0];
    }
    $site_directory = 0;
    if ($arr['directory_mode'] == 'normal') {
        $site_directory = DIRECTORY_MODE_NORMAL;
    }
    if ($arr['directory_mode'] == 'primary') {
        $site_directory = DIRECTORY_MODE_PRIMARY;
    }
    if ($arr['directory_mode'] == 'secondary') {
        $site_directory = DIRECTORY_MODE_SECONDARY;
    }
    if ($arr['directory_mode'] == 'standalone') {
        $site_directory = DIRECTORY_MODE_STANDALONE;
    }
    $register_policy = 0;
    if ($arr['register_policy'] == 'closed') {
        $register_policy = REGISTER_CLOSED;
    }
    if ($arr['register_policy'] == 'open') {
        $register_policy = REGISTER_OPEN;
    }
    if ($arr['register_policy'] == 'approve') {
        $register_policy = REGISTER_APPROVE;
    }
    $access_policy = 0;
    if (array_key_exists('access_policy', $arr)) {
        if ($arr['access_policy'] === 'private') {
            $access_policy = ACCESS_PRIVATE;
        }
        if ($arr['access_policy'] === 'paid') {
            $access_policy = ACCESS_PAID;
        }
        if ($arr['access_policy'] === 'free') {
            $access_policy = ACCESS_FREE;
        }
        if ($arr['access_policy'] === 'tiered') {
            $access_policy = ACCESS_TIERED;
        }
    }
    // don't let insecure sites register as public hubs
    if (strpos($arr['url'], 'https://') === false) {
        $access_policy = ACCESS_PRIVATE;
    }
    if ($access_policy != ACCESS_PRIVATE) {
        $x = z_fetch_url($arr['url'] . '/siteinfo/json');
        if (!$x['success']) {
            $access_policy = ACCESS_PRIVATE;
        }
    }
    $directory_url = htmlspecialchars($arr['directory_url'], ENT_COMPAT, 'UTF-8', false);
    $url = htmlspecialchars(strtolower($arr['url']), ENT_COMPAT, 'UTF-8', false);
    $sellpage = htmlspecialchars($arr['sellpage'], ENT_COMPAT, 'UTF-8', false);
    $site_location = htmlspecialchars($arr['location'], ENT_COMPAT, 'UTF-8', false);
    $site_realm = htmlspecialchars($arr['realm'], ENT_COMPAT, 'UTF-8', false);
    // You can have one and only one primary directory per realm.
    // Downgrade any others claiming to be primary. As they have
    // flubbed up this badly already, don't let them be directory servers at all.
    if ($site_directory === DIRECTORY_MODE_PRIMARY && $site_realm === get_directory_realm() && $arr['url'] != get_directory_primary()) {
        $site_directory = DIRECTORY_MODE_NORMAL;
    }
    if ($exists) {
        if ($siterecord['site_flags'] != $site_directory || $siterecord['site_access'] != $access_policy || $siterecord['site_directory'] != $directory_url || $siterecord['site_sellpage'] != $sellpage || $siterecord['site_location'] != $site_location || $siterecord['site_register'] != $register_policy || $siterecord['site_realm'] != $site_realm) {
            $update = true;
            //			logger('import_site: input: ' . print_r($arr,true));
            //			logger('import_site: stored: ' . print_r($siterecord,true));
            $r = q("update site set site_location = '%s', site_flags = %d, site_access = %d, site_directory = '%s', site_register = %d, site_update = '%s', site_sellpage = '%s', site_realm = '%s'\n\t\t\t\twhere site_url = '%s'", dbesc($site_location), intval($site_directory), intval($access_policy), dbesc($directory_url), intval($register_policy), dbesc(datetime_convert()), dbesc($sellpage), dbesc($site_realm), dbesc($url));
            if (!$r) {
                logger('import_site: update failed. ' . print_r($arr, true));
            }
        } else {
            // update the timestamp to indicate we communicated with this site
            q("update site set site_update = '%s' where site_url = '%s'", dbesc(datetime_convert()), dbesc($url));
        }
    } else {
        $update = true;
        $r = q("insert into site ( site_location, site_url, site_access, site_flags, site_update, site_directory, site_register, site_sellpage, site_realm )\n\t\t\tvalues ( '%s', '%s', %d, %d, '%s', '%s', %d, '%s', '%s' )", dbesc($site_location), dbesc($url), intval($access_policy), intval($site_directory), dbesc(datetime_convert()), dbesc($directory_url), intval($register_policy), dbesc($sellpage), dbesc($site_realm));
        if (!$r) {
            logger('import_site: record create failed. ' . print_r($arr, true));
        }
    }
    return $update;
}
Exemple #19
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.
    q("delete from verify where type = 'auth' and created < %s - INTERVAL %s", db_utcnow(), db_quoteinterval('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 = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", intval($c[0]['channel_id']), dbesc($data['secret']), dbesc($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);
        }
        $r = q("delete from verify where id = %d", intval($z[0]['id']));
        $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);
}
Exemple #20
0
function get_item_elements($x, $allow_code = false)
{
    $arr = array();
    if ($allow_code) {
        $arr['body'] = $x['body'];
    } else {
        $arr['body'] = $x['body'] ? htmlspecialchars($x['body'], ENT_COMPAT, 'UTF-8', false) : '';
    }
    $key = get_config('system', 'pubkey');
    $maxlen = get_max_import_size();
    if ($maxlen && mb_strlen($arr['body']) > $maxlen) {
        $arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8');
        logger('get_item_elements: message length exceeds max_import_size: truncated');
    }
    $arr['created'] = datetime_convert('UTC', 'UTC', $x['created']);
    $arr['edited'] = datetime_convert('UTC', 'UTC', $x['edited']);
    if ($arr['created'] > datetime_convert()) {
        $arr['created'] = datetime_convert();
    }
    if ($arr['edited'] > datetime_convert()) {
        $arr['edited'] = datetime_convert();
    }
    $arr['expires'] = x($x, 'expires') && $x['expires'] ? datetime_convert('UTC', 'UTC', $x['expires']) : NULL_DATE;
    $arr['commented'] = x($x, 'commented') && $x['commented'] ? datetime_convert('UTC', 'UTC', $x['commented']) : $arr['created'];
    $arr['comments_closed'] = x($x, 'comments_closed') && $x['comments_closed'] ? datetime_convert('UTC', 'UTC', $x['comments_closed']) : NULL_DATE;
    $arr['title'] = $x['title'] ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8', false) : '';
    if (mb_strlen($arr['title']) > 255) {
        $arr['title'] = mb_substr($arr['title'], 0, 255);
    }
    $arr['app'] = $x['app'] ? htmlspecialchars($x['app'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['route'] = $x['route'] ? htmlspecialchars($x['route'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mid'] = $x['message_id'] ? htmlspecialchars($x['message_id'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['parent_mid'] = $x['message_top'] ? htmlspecialchars($x['message_top'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['thr_parent'] = $x['message_parent'] ? htmlspecialchars($x['message_parent'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['plink'] = $x['permalink'] ? htmlspecialchars($x['permalink'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['location'] = $x['location'] ? htmlspecialchars($x['location'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['coord'] = $x['longlat'] ? htmlspecialchars($x['longlat'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['verb'] = $x['verb'] ? htmlspecialchars($x['verb'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mimetype'] = $x['mimetype'] ? htmlspecialchars($x['mimetype'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['obj_type'] = $x['object_type'] ? htmlspecialchars($x['object_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['tgt_type'] = $x['target_type'] ? htmlspecialchars($x['target_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['public_policy'] = $x['public_scope'] ? htmlspecialchars($x['public_scope'], ENT_COMPAT, 'UTF-8', false) : '';
    if ($arr['public_policy'] === 'public') {
        $arr['public_policy'] = '';
    }
    $arr['comment_policy'] = $x['comment_scope'] ? htmlspecialchars($x['comment_scope'], ENT_COMPAT, 'UTF-8', false) : 'contacts';
    $arr['sig'] = $x['signature'] ? htmlspecialchars($x['signature'], ENT_COMPAT, 'UTF-8', false) : '';
    if (array_key_exists('diaspora_signature', $x) && is_array($x['diaspora_signature'])) {
        $x['diaspora_signature'] = json_encode($x['diaspora_signature']);
    }
    $arr['diaspora_meta'] = $x['diaspora_signature'] ? $x['diaspora_signature'] : '';
    $arr['object'] = activity_sanitise($x['object']);
    $arr['target'] = activity_sanitise($x['target']);
    $arr['attach'] = activity_sanitise($x['attach']);
    $arr['term'] = decode_tags($x['tags']);
    $arr['item_private'] = array_key_exists('flags', $x) && is_array($x['flags']) && in_array('private', $x['flags']) ? 1 : 0;
    $arr['item_flags'] = 0;
    if (array_key_exists('flags', $x) && in_array('consensus', $x['flags'])) {
        $arr['item_consensus'] = 1;
    }
    if (array_key_exists('flags', $x) && in_array('deleted', $x['flags'])) {
        $arr['item_deleted'] = 1;
    }
    if (array_key_exists('flags', $x) && in_array('hidden', $x['flags'])) {
        $arr['item_hidden'] = 1;
    }
    // Here's the deal - the site might be down or whatever but if there's a new person you've never
    // seen before sending stuff to your stream, we MUST be able to look them up and import their data from their
    // hub and verify that they are legit - or else we're going to toss the post. We only need to do this
    // once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier
    // and not enough info to be able to look you up from your hash - which is the only thing stored with the post.
    if (($xchan_hash = import_author_xchan($x['author'])) !== false) {
        $arr['author_xchan'] = $xchan_hash;
    } else {
        return array();
    }
    // save a potentially expensive lookup if author == owner
    if ($arr['author_xchan'] === make_xchan_hash($x['owner']['guid'], $x['owner']['guid_sig'])) {
        $arr['owner_xchan'] = $arr['author_xchan'];
    } else {
        if (($xchan_hash = import_author_xchan($x['owner'])) !== false) {
            $arr['owner_xchan'] = $xchan_hash;
        } else {
            return array();
        }
    }
    if ($arr['sig']) {
        $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($arr['author_xchan']));
        if ($r && rsa_verify($x['body'], base64url_decode($arr['sig']), $r[0]['xchan_pubkey'])) {
            $arr['item_verified'] = 1;
        } else {
            logger('get_item_elements: message verification failed.');
        }
    }
    if (array_key_exists('revision', $x)) {
        // extended export encoding
        $arr['revision'] = $x['revision'];
        $arr['allow_cid'] = $x['allow_cid'];
        $arr['allow_gid'] = $x['allow_gid'];
        $arr['deny_cid'] = $x['deny_cid'];
        $arr['deny_gid'] = $x['deny_gid'];
        $arr['layout_mid'] = $x['layout_mid'];
        $arr['postopts'] = $x['postopts'];
        $arr['resource_id'] = $x['resource_id'];
        $arr['resource_type'] = $x['resource_type'];
        $arr['attach'] = $x['attach'];
        $arr['item_origin'] = $x['item_origin'];
        $arr['item_unseen'] = $x['item_unseen'];
        $arr['item_starred'] = $x['item_starred'];
        $arr['item_uplink'] = $x['item_uplink'];
        $arr['item_consensus'] = $x['item_consensus'];
        $arr['item_wall'] = $x['item_wall'];
        $arr['item_thread_top'] = $x['item_thread_top'];
        $arr['item_notshown'] = $x['item_notshown'];
        $arr['item_nsfw'] = $x['item_nsfw'];
        // local only		$arr['item_relay'] = $x['item_relay'];
        $arr['item_mentionsme'] = $x['item_mentionsme'];
        $arr['item_nocomment'] = $x['item_nocomment'];
        // local only $arr['item_obscured'] = $x['item_obscured'];
        // local only $arr['item_verified'] = $x['item_verified'];
        $arr['item_retained'] = $x['item_retained'];
        $arr['item_rss'] = $x['item_rss'];
        $arr['item_deleted'] = $x['item_deleted'];
        $arr['item_type'] = $x['item_type'];
        $arr['item_hidden'] = $x['item_hidden'];
        $arr['item_unpublished'] = $x['item_unpublished'];
        $arr['item_delayed'] = $x['item_delayed'];
        $arr['item_pending_remove'] = $x['item_pending_remove'];
        $arr['item_blocked'] = $x['item_blocked'];
        if (array_key_exists('item_flags', $x)) {
            if ($x['item_flags'] & 0x4) {
                $arr['item_starred'] = 1;
            }
            if ($x['item_flags'] & 0x8) {
                $arr['item_uplink'] = 1;
            }
            if ($x['item_flags'] & 0x10) {
                $arr['item_consensus'] = 1;
            }
            if ($x['item_flags'] & 0x20) {
                $arr['item_wall'] = 1;
            }
            if ($x['item_flags'] & 0x40) {
                $arr['item_thread_top'] = 1;
            }
            if ($x['item_flags'] & 0x80) {
                $arr['item_notshown'] = 1;
            }
            if ($x['item_flags'] & 0x100) {
                $arr['item_nsfw'] = 1;
            }
            if ($x['item_flags'] & 0x400) {
                $arr['item_mentionsme'] = 1;
            }
            if ($x['item_flags'] & 0x800) {
                $arr['item_nocomment'] = 1;
            }
            if ($x['item_flags'] & 0x4000) {
                $arr['item_retained'] = 1;
            }
            if ($x['item_flags'] & 0x8000) {
                $arr['item_rss'] = 1;
            }
        }
        if (array_key_exists('item_restrict', $x)) {
            if ($x['item_restrict'] & 0x1) {
                $arr['item_hidden'] = 1;
            }
            if ($x['item_restrict'] & 0x2) {
                $arr['item_blocked'] = 1;
            }
            if ($x['item_restrict'] & 0x10) {
                $arr['item_deleted'] = 1;
            }
            if ($x['item_restrict'] & 0x20) {
                $arr['item_unpublished'] = 1;
            }
            if ($x['item_restrict'] & 0x40) {
                $arr['item_type'] = ITEM_TYPE_WEBPAGE;
            }
            if ($x['item_restrict'] & 0x80) {
                $arr['item_delayed'] = 1;
            }
            if ($x['item_restrict'] & 0x100) {
                $arr['item_type'] = ITEM_TYPE_BLOCK;
            }
            if ($x['item_restrict'] & 0x200) {
                $arr['item_type'] = ITEM_TYPE_PDL;
            }
            if ($x['item_restrict'] & 0x400) {
                $arr['item_type'] = ITEM_TYPE_BUG;
            }
            if ($x['item_restrict'] & 0x800) {
                $arr['item_pending_remove'] = 1;
            }
            if ($x['item_restrict'] & 0x1000) {
                $arr['item_type'] = ITEM_TYPE_DOC;
            }
        }
    }
    return $arr;
}
Exemple #21
0
include_once "log.php";
include_once "recharge.php";
include_once "ssl.php";
$config = (include "config.php");
header("Content-type: text/html; charset=utf-8");
log::init('./log', 'wdj_log');
$uri = $_SERVER['REQUEST_URI'];
$body = file_get_contents('php://input');
log::instance()->debug("new con: {$uri} {$body}");
$AppID = "100013257";
$pf_info = $config["wdj"][$AppID];
// RSA verify
$content = stripslashes($_POST["content"]);
$sign = base64_decode(stripslashes($_POST["sign"]));
$pem = chunk_split($pf_info["PubKey"], 64, "\n");
$pubkey = "-----BEGIN PUBLIC KEY-----\n" . $pem . "-----END PUBLIC KEY-----";
if (!rsa_verify($content, $sign, $pubkey)) {
    log::instance()->error("ret: 签名无效");
    echo "fail";
    exit;
}
// end verify
$content = json_decode($content, true);
$note = json_decode($content["out_trade_no"], true);
$ret = recharge($pf_info["PF"], $note["sid"], $note["odr"], $note["uid"], $note["item"], $content["money"] / 100, $content["orderId"], 0);
log::instance()->debug("ret: " . $ret);
if ($ret == "SUCCESS" || $ret == "TRADE_NO NOT EXIST") {
    echo "success";
} else {
    echo "fail";
}
Exemple #22
0
function get_item_elements($x)
{
    $arr = array();
    $arr['body'] = $x['body'] ? htmlspecialchars($x['body'], ENT_COMPAT, 'UTF-8', false) : '';
    $key = get_config('system', 'pubkey');
    $maxlen = get_max_import_size();
    if ($maxlen && mb_strlen($arr['body']) > $maxlen) {
        $arr['body'] = mb_substr($arr['body'], 0, $maxlen, 'UTF-8');
        logger('get_item_elements: message length exceeds max_import_size: truncated');
    }
    $arr['created'] = datetime_convert('UTC', 'UTC', $x['created']);
    $arr['edited'] = datetime_convert('UTC', 'UTC', $x['edited']);
    if ($arr['created'] > datetime_convert()) {
        $arr['created'] = datetime_convert();
    }
    if ($arr['edited'] > datetime_convert()) {
        $arr['edited'] = datetime_convert();
    }
    $arr['expires'] = x($x, 'expires') && $x['expires'] ? datetime_convert('UTC', 'UTC', $x['expires']) : NULL_DATE;
    $arr['commented'] = x($x, 'commented') && $x['commented'] ? datetime_convert('UTC', 'UTC', $x['commented']) : $arr['created'];
    $arr['comments_closed'] = x($x, 'comments_closed') && $x['comments_closed'] ? datetime_convert('UTC', 'UTC', $x['comments_closed']) : NULL_DATE;
    $arr['title'] = $x['title'] ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8', false) : '';
    if (mb_strlen($arr['title']) > 255) {
        $arr['title'] = mb_substr($arr['title'], 0, 255);
    }
    $arr['app'] = $x['app'] ? htmlspecialchars($x['app'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['route'] = $x['route'] ? htmlspecialchars($x['route'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mid'] = $x['message_id'] ? htmlspecialchars($x['message_id'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['parent_mid'] = $x['message_top'] ? htmlspecialchars($x['message_top'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['thr_parent'] = $x['message_parent'] ? htmlspecialchars($x['message_parent'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['plink'] = $x['permalink'] ? htmlspecialchars($x['permalink'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['location'] = $x['location'] ? htmlspecialchars($x['location'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['coord'] = $x['longlat'] ? htmlspecialchars($x['longlat'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['verb'] = $x['verb'] ? htmlspecialchars($x['verb'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['mimetype'] = $x['mimetype'] ? htmlspecialchars($x['mimetype'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['obj_type'] = $x['object_type'] ? htmlspecialchars($x['object_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['tgt_type'] = $x['target_type'] ? htmlspecialchars($x['target_type'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['public_policy'] = $x['public_scope'] ? htmlspecialchars($x['public_scope'], ENT_COMPAT, 'UTF-8', false) : '';
    if ($arr['public_policy'] === 'public') {
        $arr['public_policy'] = '';
    }
    $arr['comment_policy'] = $x['comment_scope'] ? htmlspecialchars($x['comment_scope'], ENT_COMPAT, 'UTF-8', false) : 'contacts';
    $arr['sig'] = $x['signature'] ? htmlspecialchars($x['signature'], ENT_COMPAT, 'UTF-8', false) : '';
    $arr['diaspora_meta'] = $x['diaspora_signature'] ? json_encode(crypto_encapsulate($x['diaspora_signature'], $key)) : '';
    $arr['object'] = activity_sanitise($x['object']);
    $arr['target'] = activity_sanitise($x['target']);
    $arr['attach'] = activity_sanitise($x['attach']);
    $arr['term'] = decode_tags($x['tags']);
    $arr['item_private'] = array_key_exists('flags', $x) && is_array($x['flags']) && in_array('private', $x['flags']) ? 1 : 0;
    $arr['item_flags'] = 0;
    if (array_key_exists('flags', $x) && in_array('consensus', $x['flags'])) {
        $arr['item_flags'] |= ITEM_CONSENSUS;
    }
    if (array_key_exists('flags', $x) && in_array('deleted', $x['flags'])) {
        $arr['item_restrict'] |= ITEM_DELETED;
    }
    if (array_key_exists('flags', $x) && in_array('hidden', $x['flags'])) {
        $arr['item_restrict'] |= ITEM_HIDDEN;
    }
    // Here's the deal - the site might be down or whatever but if there's a new person you've never
    // seen before sending stuff to your stream, we MUST be able to look them up and import their data from their
    // hub and verify that they are legit - or else we're going to toss the post. We only need to do this
    // once, and after that your hub knows them. Sure some info is in the post, but it's only a transit identifier
    // and not enough info to be able to look you up from your hash - which is the only thing stored with the post.
    if (($xchan_hash = import_author_xchan($x['author'])) !== false) {
        $arr['author_xchan'] = $xchan_hash;
    } else {
        return array();
    }
    // save a potentially expensive lookup if author == owner
    if ($arr['author_xchan'] === make_xchan_hash($x['owner']['guid'], $x['owner']['guid_sig'])) {
        $arr['owner_xchan'] = $arr['author_xchan'];
    } else {
        if (($xchan_hash = import_author_xchan($x['owner'])) !== false) {
            $arr['owner_xchan'] = $xchan_hash;
        } else {
            return array();
        }
    }
    if ($arr['sig']) {
        $r = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", dbesc($arr['author_xchan']));
        if ($r && rsa_verify($x['body'], base64url_decode($arr['sig']), $r[0]['xchan_pubkey'])) {
            $arr['item_flags'] |= ITEM_VERIFIED;
        } else {
            logger('get_item_elements: message verification failed.');
        }
    }
    // if it's a private post, encrypt it in the DB.
    // We have to do that here because we need to cleanse the input and prevent bad stuff from getting in,
    // and we need plaintext to do that.
    if (intval($arr['item_private'])) {
        $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED;
        if ($arr['title']) {
            $arr['title'] = json_encode(crypto_encapsulate($arr['title'], $key));
        }
        if ($arr['body']) {
            $arr['body'] = json_encode(crypto_encapsulate($arr['body'], $key));
        }
    }
    if (array_key_exists('revision', $x)) {
        // extended export encoding
        $arr['revision'] = $x['revision'];
        $arr['allow_cid'] = $x['allow_cid'];
        $arr['allow_gid'] = $x['allow_gid'];
        $arr['deny_cid'] = $x['deny_cid'];
        $arr['deny_gid'] = $x['deny_gid'];
        $arr['layout_mid'] = $x['layout_mid'];
        $arr['postopts'] = $x['postopts'];
        $arr['resource_id'] = $x['resource_id'];
        $arr['resource_type'] = $x['resource_type'];
        $arr['item_restrict'] = $x['item_restrict'];
        $arr['item_flags'] = $x['item_flags'];
        $arr['attach'] = $x['attach'];
    }
    return $arr;
}
Exemple #23
0
function diaspora_signed_retraction($importer, $xml, $msg)
{
    $guid = notags(unxmlify($xml->target_guid));
    $diaspora_handle = notags(unxmlify($xml->sender_handle));
    $type = notags(unxmlify($xml->target_type));
    $sig = notags(unxmlify($xml->target_author_signature));
    $parent_author_signature = $xml->parent_author_signature ? notags(unxmlify($xml->parent_author_signature)) : '';
    $contact = diaspora_get_contact_by_handle($importer['uid'], $diaspora_handle);
    if (!$contact) {
        logger('diaspora_signed_retraction: no contact');
        return;
    }
    $signed_data = $guid . ';' . $type;
    $sig_decode = base64_decode($sig);
    if (strcasecmp($diaspora_handle, $msg['author']) == 0) {
        $person = $contact;
        $key = $msg['key'];
    } else {
        $person = find_diaspora_person_by_handle($diaspora_handle);
        if (is_array($person) && x($person, 'pubkey')) {
            $key = $person['pubkey'];
        } else {
            logger('diaspora_signed_retraction: unable to find author details');
            return;
        }
    }
    if (!rsa_verify($signed_data, $sig_decode, $key, 'sha256')) {
        logger('diaspora_signed_retraction: retraction-owner verification failed.' . print_r($msg, true));
        return;
    }
    if ($parent_author_signature) {
        $parent_author_signature = base64_decode($parent_author_signature);
        $key = $msg['key'];
        if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256')) {
            logger('diaspora_signed_retraction: failed to verify person relaying the retraction (e.g. owner of a post relaying a retracted comment');
            return;
        }
    }
    if ($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
        $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1", dbesc($guid), intval($importer['uid']));
        if (count($r)) {
            if (link_compare($r[0]['author-link'], $contact['url'])) {
                q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d limit 1", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['id']));
                // Now check if the retraction needs to be relayed by us
                //
                // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
                // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
                // The only item with `parent` and `id` as the parent id is the parent item.
                $p = q("select origin from item where parent = %d and id = %d limit 1", $r[0]['parent'], $r[0]['parent']);
                if (count($p)) {
                    if ($p[0]['origin'] && !$parent_author_signature) {
                        q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", $r[0]['id'], dbesc($signed_data), dbesc($sig), dbesc($diaspora_handle));
                        // the existence of parent_author_signature would have meant the parent_author or owner
                        // is already relaying.
                        logger('diaspora_signed_retraction: relaying relayable_retraction');
                        proc_run('php', 'include/notifier.php', 'relayable_retraction', $r[0]['id']);
                    }
                }
            }
        }
    } else {
        logger('diaspora_signed_retraction: unknown type: ' . $type);
    }
    return 202;
    // NOTREACHED
}
Exemple #24
0
function diaspora_signed_retraction($importer, $xml, $msg)
{
    $guid = notags(unxmlify($xml->target_guid));
    $diaspora_handle = notags(unxmlify($xml->sender_handle));
    $type = notags(unxmlify($xml->target_type));
    $sig = notags(unxmlify($xml->target_author_signature));
    $parent_author_signature = $xml->parent_author_signature ? notags(unxmlify($xml->parent_author_signature)) : '';
    $contact = diaspora_get_contact_by_handle($importer['uid'], $diaspora_handle);
    if (!$contact) {
        logger('diaspora_signed_retraction: no contact ' . $diaspora_handle . ' for ' . $importer['uid']);
        return;
    }
    $signed_data = $guid . ';' . $type;
    $key = $msg['key'];
    /* How Diaspora performs relayable_retraction signature checking:
    
    	   - If an item has been sent by the item author to the top-level post owner to relay on
    	     to the rest of the contacts on the top-level post, the top-level post owner checks
    	     the author_signature, then creates a parent_author_signature before relaying the item on
    	   - If an item has been relayed on by the top-level post owner, the contacts who receive it
    	     check only the parent_author_signature. Basically, they trust that the top-level post
    	     owner has already verified the authenticity of anything he/she sends out
    	   - In either case, the signature that get checked is the signature created by the person
    	     who sent the salmon
    	*/
    if ($parent_author_signature) {
        $parent_author_signature = base64_decode($parent_author_signature);
        if (!rsa_verify($signed_data, $parent_author_signature, $key, 'sha256')) {
            logger('diaspora_signed_retraction: top-level post owner verification failed');
            return;
        }
    } else {
        $sig_decode = base64_decode($sig);
        if (!rsa_verify($signed_data, $sig_decode, $key, 'sha256')) {
            logger('diaspora_signed_retraction: retraction owner verification failed.' . print_r($msg, true));
            return;
        }
    }
    if ($type === 'StatusMessage' || $type === 'Comment' || $type === 'Like') {
        $r = q("select * from item where guid = '%s' and uid = %d and not file like '%%[%%' limit 1", dbesc($guid), intval($importer['uid']));
        if (count($r)) {
            if (link_compare($r[0]['author-link'], $contact['url'])) {
                q("update item set `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = '' where `id` = %d", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($r[0]['id']));
                delete_thread($r[0]['id'], $r[0]['parent-uri']);
                // Now check if the retraction needs to be relayed by us
                //
                // The first item in the `item` table with the parent id is the parent. However, MySQL doesn't always
                // return the items ordered by `item`.`id`, in which case the wrong item is chosen as the parent.
                // The only item with `parent` and `id` as the parent id is the parent item.
                $p = q("select origin from item where parent = %d and id = %d limit 1", $r[0]['parent'], $r[0]['parent']);
                if (count($p)) {
                    if ($p[0]['origin'] && !$parent_author_signature) {
                        q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ", $r[0]['id'], dbesc($signed_data), dbesc($sig), dbesc($diaspora_handle));
                        // the existence of parent_author_signature would have meant the parent_author or owner
                        // is already relaying.
                        logger('diaspora_signed_retraction: relaying relayable_retraction');
                        proc_run('php', 'include/notifier.php', 'drop', $r[0]['id']);
                    }
                }
            }
        }
    } else {
        logger('diaspora_signed_retraction: unknown type: ' . $type);
    }
    return 202;
    // NOTREACHED
}
Exemple #25
0
function salmon_post(&$a)
{
    $sys_disabled = true;
    if (!get_config('system', 'disable_discover_tab')) {
        $sys_disabled = get_config('system', 'disable_diaspora_discover_tab');
    }
    $sys = $sys_disabled ? null : get_sys_channel();
    if (App::$data['salmon_test']) {
        $xml = file_get_contents('test.xml');
        App::$argv[1] = 'gnusoc';
    } else {
        $xml = file_get_contents('php://input');
    }
    logger('mod-salmon: new salmon ' . $xml, LOGGER_DATA);
    $nick = argc() > 1 ? trim(argv(1)) : '';
    //	$mentions   = ((App::$argc > 2 && App::$argv[2] === 'mention') ? true : false);
    $importer = channelx_by_nick($nick);
    if (!$importer) {
        http_status_exit(500);
    }
    // @fixme check that this channel has the GNU-Social protocol enabled
    // parse the xml
    $dom = simplexml_load_string($xml, 'SimpleXMLElement', 0, NAMESPACE_SALMON_ME);
    // figure out where in the DOM tree our data is hiding
    if ($dom->provenance->data) {
        $base = $dom->provenance;
    } elseif ($dom->env->data) {
        $base = $dom->env;
    } elseif ($dom->data) {
        $base = $dom;
    }
    if (!$base) {
        logger('mod-salmon: unable to locate salmon data in xml ');
        http_status_exit(400);
    }
    logger('data: ' . $xml, LOGGER_DATA);
    // Stash the signature away for now. We have to find their key or it won't be good for anything.
    logger('sig: ' . $base->sig);
    $signature = base64url_decode($base->sig);
    logger('sig: ' . $base->sig . ' decoded length: ' . strlen($signature));
    // unpack the  data
    // strip whitespace so our data element will return to one big base64 blob
    $data = str_replace(array(" ", "\t", "\r", "\n"), array("", "", "", ""), $base->data);
    // stash away some other stuff for later
    $type = $base->data[0]->attributes()->type[0];
    $keyhash = $base->sig[0]->attributes()->keyhash[0];
    $encoding = $base->encoding;
    $alg = $base->alg;
    // Salmon magic signatures have evolved and there is no way of knowing ahead of time which
    // flavour we have. We'll try and verify it regardless.
    $stnet_signed_data = $data;
    $signed_data = $data . '.' . base64url_encode($type, false) . '.' . base64url_encode($encoding, false) . '.' . base64url_encode($alg, false);
    $compliant_format = str_replace('=', '', $signed_data);
    // decode the data
    $data = base64url_decode($data);
    logger('decoded: ' . $data, LOGGER_DATA);
    // GNU-Social doesn't send a legal Atom feed over salmon, only an Atom entry. Unfortunately
    // our parser is a bit strict about compliance so we'll insert just enough of a feed
    // tag to trick it into believing it's a compliant feed.
    if (!strstr($data, '<feed')) {
        $data = str_replace('<entry ', '<feed xmlns="http://www.w3.org/2005/Atom"><entry ', $data);
        $data .= '</feed>';
    }
    $datarray = process_salmon_feed($data, $importer);
    $author_link = $datarray['author']['author_link'];
    $item = $datarray['item'];
    if (!$author_link) {
        logger('mod-salmon: Could not retrieve author URI.');
        http_status_exit(400);
    }
    $r = q("select xchan_pubkey from xchan where xchan_guid = '%s' limit 1", dbesc($author_link));
    if ($r) {
        $pubkey = $r[0]['xchan_pubkey'];
    } else {
        // Once we have the author URI, go to the web and try to find their public key
        logger('mod-salmon: Fetching key for ' . $author_link);
        $pubkey = get_salmon_key($author_link, $keyhash);
        if (!$pubkey) {
            logger('mod-salmon: Could not retrieve author key.');
            http_status_exit(400);
        }
        logger('mod-salmon: key details: ' . print_r($pubkey, true), LOGGER_DEBUG);
    }
    $pubkey = rtrim($pubkey);
    // We should have everything we need now. Let's see if it verifies.
    $verify = rsa_verify($signed_data, $signature, $pubkey);
    if (!$verify) {
        logger('mod-salmon: message did not verify using protocol. Trying padding hack.');
        $verify = rsa_verify($compliant_format, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: message did not verify using padding. Trying old statusnet hack.');
        $verify = rsa_verify($stnet_signed_data, $signature, $pubkey);
    }
    if (!$verify) {
        logger('mod-salmon: Message did not verify. Discarding.');
        http_status_exit(400);
    }
    logger('mod-salmon: Message verified.');
    /* lookup the author */
    if (!$datarray['author']['author_link']) {
        logger('unable to probe - no author identifier');
        http_status_exit(400);
    }
    $r = q("select * from xchan where xchan_guid = '%s' limit 1", dbesc($datarray['author']['author_link']));
    if (!$r) {
        if (discover_by_webbie($datarray['author']['author_link'])) {
            $r = q("select * from xchan where xchan_guid = '%s' limit 1", dbesc($datarray['author']['author_link']));
            if (!$r) {
                logger('discovery failed');
                http_status_exit(400);
            }
        }
    }
    $xchan = $r[0];
    /*
     *
     * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
     *
     */
    // First check for and process follow activity
    if (activity_match($item['verb'], ACTIVITY_FOLLOW) && $item['obj_type'] === ACTIVITY_OBJ_PERSON) {
        $cb = array('item' => $item, 'channel' => $importer, 'xchan' => $xchan, 'author' => $datarray['author'], 'caught' => false);
        call_hooks('follow_from_feed', $cb);
        if ($cb['caught']) {
            http_status_exit(200);
        }
    }
    $m = parse_url($xchan['xchan_url']);
    if ($m) {
        $host = $m['scheme'] . '://' . $m['host'];
        q("update site set site_dead = 0, site_update = '%s' where site_type = %d and site_url = '%s'", dbesc(datetime_convert()), intval(SITE_TYPE_NOTZOT), dbesc($url));
        if (!check_siteallowed($host)) {
            logger('blacklisted site: ' . $host);
            http_status_exit(403, 'permission denied.');
        }
    }
    $importer_arr = array($importer);
    if (!$sys_disabled) {
        $sys['system'] = true;
        $importer_arr[] = $sys;
    }
    unset($datarray['author']);
    // we will only set and return the status code for operations
    // on an importer channel and not for the sys channel
    $status = 200;
    foreach ($importer_arr as $importer) {
        if (!$importer['system']) {
            $allowed = get_pconfig($importer['channel_id'], 'system', 'gnusoc_allowed');
            if (!intval($allowed)) {
                logger('mod-salmon: disallowed for channel ' . $importer['channel_name']);
                $status = 202;
                continue;
            }
        }
        // Otherwise check general permissions
        if (!perm_is_allowed($importer['channel_id'], $xchan['xchan_hash'], 'send_stream') && !$importer['system']) {
            // check for and process ostatus autofriend
            // ... fixme
            // otherwise
            logger('mod-salmon: Ignoring this author.');
            $status = 202;
            continue;
        }
        $parent_item = null;
        if ($item['parent_mid']) {
            $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($item['parent_mid']), intval($importer['channel_id']));
            if (!$r) {
                logger('mod-salmon: parent item not found.');
                if (!$importer['system']) {
                    $status = 202;
                }
                continue;
            }
            $parent_item = $r[0];
        }
        if (!$item['author_xchan']) {
            $item['author_xchan'] = $xchan['xchan_hash'];
        }
        $item['owner_xchan'] = $parent_item ? $parent_item['owner_xchan'] : $xchan['xchan_hash'];
        $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($item['mid']), intval($importer['channel_id']));
        // Update content if 'updated' changes
        // currently a no-op @fixme
        if ($r) {
            if (x($item, 'edited') !== false && datetime_convert('UTC', 'UTC', $item['edited']) !== $r[0]['edited']) {
                // do not accept (ignore) an earlier edit than one we currently have.
                if (datetime_convert('UTC', 'UTC', $item['edited']) > $r[0]['edited']) {
                    update_feed_item($importer['channel_id'], $item);
                }
            }
            if (!$importer['system']) {
                $status = 200;
            }
            continue;
        }
        if (!$item['parent_mid']) {
            $item['parent_mid'] = $item['mid'];
        }
        $item['aid'] = $importer['channel_account_id'];
        $item['uid'] = $importer['channel_id'];
        logger('consume_feed: ' . print_r($item, true), LOGGER_DATA);
        $xx = item_store($item);
        $r = $xx['item_id'];
        if (!$importer['system']) {
            $status = 200;
        }
        continue;
    }
    http_status_exit($status);
}