security updates for multiple xchans
This commit is contained in:
parent
4e97fb0e58
commit
5fb0d38ad8
@ -306,6 +306,7 @@ function change_channel($change_channel) {
|
|||||||
*
|
*
|
||||||
* @return string additional SQL where statement
|
* @return string additional SQL where statement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function permissions_sql($owner_id, $remote_observer = null, $table = '') {
|
function permissions_sql($owner_id, $remote_observer = null, $table = '') {
|
||||||
|
|
||||||
$local_channel = local_channel();
|
$local_channel = local_channel();
|
||||||
@ -316,7 +317,7 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') {
|
|||||||
* default permissions - anonymous user
|
* default permissions - anonymous user
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if($table)
|
if ($table)
|
||||||
$table .= '.';
|
$table .= '.';
|
||||||
|
|
||||||
$sql = " AND {$table}allow_cid = ''
|
$sql = " AND {$table}allow_cid = ''
|
||||||
@ -329,38 +330,63 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') {
|
|||||||
* Profile owner - everything is visible
|
* Profile owner - everything is visible
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if(($local_channel) && ($local_channel == $owner_id)) {
|
if (($local_channel) && ($local_channel == $owner_id)) {
|
||||||
$sql = '';
|
return EMPTY_STR;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticated visitor. Unless pre-verified,
|
* Authenticated visitor.
|
||||||
* check that the contact belongs to this $owner_id
|
|
||||||
* and load the groups the visitor belongs to.
|
|
||||||
* If pre-verified, the caller is expected to have already
|
|
||||||
* done this and passed the groups into this function.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else {
|
else {
|
||||||
|
|
||||||
$observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash());
|
$observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash());
|
||||||
if($observer) {
|
|
||||||
$groups = init_groups_visitor($observer);
|
|
||||||
|
|
||||||
$gs = '<<>>'; // should be impossible to match
|
if ($observer) {
|
||||||
|
|
||||||
if(is_array($groups) && count($groups)) {
|
$sec = get_security_ids($owner_id,$observer);
|
||||||
foreach($groups as $g)
|
|
||||||
$gs .= '|<' . $g . '>';
|
// always allow the channel owner, even if authenticated as a visitor
|
||||||
|
|
||||||
|
if ($sec['channel_id']) {
|
||||||
|
foreach ($sec['channel_id'] as $ch) {
|
||||||
|
if ($observer === $ch) {
|
||||||
|
return EMPTY_STR;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) {
|
||||||
|
$ca = [];
|
||||||
|
foreach ($sec['allow_cid'] as $c) {
|
||||||
|
$ca[] = '<' . $c . '>';
|
||||||
|
}
|
||||||
|
$cs = implode('|',$ca);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$cs = '<<>>'; // should be impossible to match
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) {
|
||||||
|
$ga = [];
|
||||||
|
foreach ($sec['allow_gid'] as $g) {
|
||||||
|
$ga[] = '<' . $g . '>';
|
||||||
|
}
|
||||||
|
$gs = implode('|',$ga);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$gs = '<<>>'; // should be impossible to match
|
||||||
|
}
|
||||||
|
|
||||||
$regexop = db_getfunc('REGEXP');
|
$regexop = db_getfunc('REGEXP');
|
||||||
$sql = sprintf(
|
$sql = sprintf(
|
||||||
" AND ( NOT ({$table}deny_cid like '%s' OR {$table}deny_gid $regexop '%s')
|
" AND ( NOT ({$table}deny_cid regexop '%s' OR {$table}deny_gid $regexop '%s')
|
||||||
AND ( {$table}allow_cid like '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') )
|
AND ( {$table}allow_cid regexop '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') )
|
||||||
)
|
)
|
||||||
",
|
",
|
||||||
dbesc(protect_sprintf( '%<' . $observer . '>%')),
|
dbesc($cs),
|
||||||
dbesc($gs),
|
dbesc($gs),
|
||||||
dbesc(protect_sprintf( '%<' . $observer . '>%')),
|
dbesc($cs),
|
||||||
dbesc($gs)
|
dbesc($gs)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -377,6 +403,7 @@ function permissions_sql($owner_id, $remote_observer = null, $table = '') {
|
|||||||
*
|
*
|
||||||
* @return string additional SQL where statement
|
* @return string additional SQL where statement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function item_permissions_sql($owner_id, $remote_observer = null) {
|
function item_permissions_sql($owner_id, $remote_observer = null) {
|
||||||
|
|
||||||
$local_channel = local_channel();
|
$local_channel = local_channel();
|
||||||
@ -398,37 +425,59 @@ function item_permissions_sql($owner_id, $remote_observer = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticated visitor. Unless pre-verified,
|
* Authenticated visitor.
|
||||||
* check that the contact belongs to this $owner_id
|
|
||||||
* and load the groups the visitor belongs to.
|
|
||||||
* If pre-verified, the caller is expected to have already
|
|
||||||
* done this and passed the groups into this function.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
else {
|
else {
|
||||||
$observer = (($remote_observer) ? $remote_observer : get_observer_hash());
|
|
||||||
|
|
||||||
if($observer) {
|
$observer = (($remote_observer) ? $remote_observer : get_observer_hash());
|
||||||
|
|
||||||
$s = scopes_sql($owner_id,$observer);
|
if($observer) {
|
||||||
|
|
||||||
$groups = init_groups_visitor($observer);
|
$scope = scopes_sql($owner_id,$observer);
|
||||||
|
$sec = get_security_ids($owner_id,$observer);
|
||||||
|
|
||||||
$gs = '<<>>'; // should be impossible to match
|
// always allow the channel owner, even if authenticated as a visitor
|
||||||
|
|
||||||
if(is_array($groups) && count($groups)) {
|
if($sec['channel_id']) {
|
||||||
foreach($groups as $g)
|
foreach($sec['channel_id'] as $ch) {
|
||||||
$gs .= '|<' . $g . '>';
|
if($observer === $ch) {
|
||||||
|
return EMPTY_STR;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) {
|
||||||
|
$ca = [];
|
||||||
|
foreach ($sec['allow_cid'] as $c) {
|
||||||
|
$ca[] = '<' . $c . '>';
|
||||||
|
}
|
||||||
|
$cs = implode('|',$ca);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$cs = '<<>>'; // should be impossible to match
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) {
|
||||||
|
$ga = [];
|
||||||
|
foreach ($sec['allow_gid'] as $g) {
|
||||||
|
$ga[] = '<' . $g . '>';
|
||||||
|
}
|
||||||
|
$gs = implode('|',$ga);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$gs = '<<>>'; // should be impossible to match
|
||||||
|
}
|
||||||
|
|
||||||
$regexop = db_getfunc('REGEXP');
|
$regexop = db_getfunc('REGEXP');
|
||||||
$sql = sprintf(
|
$sql = sprintf(
|
||||||
" AND (( NOT (deny_cid like '%s' OR deny_gid $regexop '%s')
|
" AND (( NOT (deny_cid regexop '%s' OR deny_gid $regexop '%s')
|
||||||
AND ( allow_cid like '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 ))
|
AND ( allow_cid regexop '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 ))
|
||||||
) OR ( item_private = 1 $s ))
|
) OR ( item_private = 1 $scope ))
|
||||||
",
|
",
|
||||||
dbesc(protect_sprintf( '%<' . $observer . '>%')),
|
dbesc($cs),
|
||||||
dbesc($gs),
|
dbesc($gs),
|
||||||
dbesc(protect_sprintf( '%<' . $observer . '>%')),
|
dbesc($cs),
|
||||||
dbesc($gs)
|
dbesc($gs)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -465,40 +514,57 @@ function scopes_sql($uid,$observer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $observer_hash
|
* @param string $observer_hash
|
||||||
*
|
*
|
||||||
* @return string additional SQL where statement
|
* @return string additional SQL where statement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function public_permissions_sql($observer_hash) {
|
function public_permissions_sql($observer_hash) {
|
||||||
|
|
||||||
$groups = init_groups_visitor($observer_hash);
|
$owner_id = 0;
|
||||||
|
|
||||||
$gs = '<<>>'; // should be impossible to match
|
if ($observer_hash) {
|
||||||
|
|
||||||
|
$sec = get_security_ids($owner_id,$observer_hash);
|
||||||
|
|
||||||
|
if (is_array($sec['allow_cid']) && count($sec['allow_cid'])) {
|
||||||
|
$ca = [];
|
||||||
|
foreach ($sec['allow_cid'] as $c) {
|
||||||
|
$ca[] = '<' . $c . '>';
|
||||||
|
}
|
||||||
|
$cs = implode('|',$ca);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$cs = '<<>>'; // should be impossible to match
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_array($sec['allow_gid']) && count($sec['allow_gid'])) {
|
||||||
|
$ga = [];
|
||||||
|
foreach ($sec['allow_gid'] as $g) {
|
||||||
|
$ga[] = '<' . $g . '>';
|
||||||
|
}
|
||||||
|
$gs = implode('|',$ga);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$gs = '<<>>'; // should be impossible to match
|
||||||
|
}
|
||||||
|
|
||||||
if(is_array($groups) && count($groups)) {
|
|
||||||
foreach($groups as $g)
|
|
||||||
$gs .= '|<' . $g . '>';
|
|
||||||
}
|
|
||||||
$sql = '';
|
|
||||||
if($observer_hash) {
|
|
||||||
$regexop = db_getfunc('REGEXP');
|
$regexop = db_getfunc('REGEXP');
|
||||||
$sql = sprintf(
|
$sql = sprintf(
|
||||||
" OR (( NOT (deny_cid like '%s' OR deny_gid $regexop '%s')
|
" AND ( NOT (deny_cid regexop '%s' OR deny_gid $regexop '%s')
|
||||||
AND ( allow_cid like '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0 ) )
|
AND ( allow_cid regexop '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '' AND item_private = 0) )
|
||||||
))
|
)
|
||||||
",
|
",
|
||||||
dbesc(protect_sprintf( '%<' . $observer_hash . '>%')),
|
dbesc($cs),
|
||||||
dbesc($gs),
|
dbesc($gs),
|
||||||
dbesc(protect_sprintf( '%<' . $observer_hash . '>%')),
|
dbesc($cs),
|
||||||
dbesc($gs)
|
dbesc($gs)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
$sql = EMPTY_STR;
|
||||||
|
}
|
||||||
|
|
||||||
return $sql;
|
return $sql;
|
||||||
}
|
}
|
||||||
@ -510,7 +576,7 @@ function public_permissions_sql($observer_hash) {
|
|||||||
* In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
|
* In this implementation, a security token is reusable (if the user submits a form, goes back and resubmits the form, maybe with small changes;
|
||||||
* or if the security token is used for ajax-calls that happen several times), but only valid for a certain amout of time (3hours).
|
* or if the security token is used for ajax-calls that happen several times), but only valid for a certain amout of time (3hours).
|
||||||
* The "typename" seperates the security tokens of different types of forms. This could be relevant in the following case:
|
* The "typename" seperates the security tokens of different types of forms. This could be relevant in the following case:
|
||||||
* A security token is used to protekt a link from CSRF (e.g. the "delete this profile"-link).
|
* A security token is used to protect a link from CSRF (e.g. the "delete this profile"-link).
|
||||||
* If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
|
* If the new page contains by any chance external elements, then the used security token is exposed by the referrer.
|
||||||
* Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are,
|
* Actually, important actions should not be triggered by Links / GET-Requests at all, but somethimes they still are,
|
||||||
* so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
|
* so this mechanism brings in some damage control (the attacker would be able to forge a request to a form of this type, but not to forms of other types).
|
||||||
@ -587,8 +653,8 @@ function init_groups_visitor($contact_id) {
|
|||||||
// private profiles are treated as a virtual group
|
// private profiles are treated as a virtual group
|
||||||
|
|
||||||
$r = q("SELECT abook_profile from abook where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_profile != '' ");
|
$r = q("SELECT abook_profile from abook where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_profile != '' ");
|
||||||
if($r) {
|
if ($r) {
|
||||||
foreach($r as $rv) {
|
foreach ($r as $rv) {
|
||||||
$groups[] = 'vp.' . $rv['abook_profile'];
|
$groups[] = 'vp.' . $rv['abook_profile'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -596,8 +662,8 @@ function init_groups_visitor($contact_id) {
|
|||||||
// physical groups this identity is a member of
|
// physical groups this identity is a member of
|
||||||
|
|
||||||
$r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) ");
|
$r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) ");
|
||||||
if($r) {
|
if ($r) {
|
||||||
foreach($r as $rr)
|
foreach ($r as $rr)
|
||||||
$groups[] = $rr['hash'];
|
$groups[] = $rr['hash'];
|
||||||
}
|
}
|
||||||
return $groups;
|
return $groups;
|
||||||
@ -605,6 +671,70 @@ function init_groups_visitor($contact_id) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function get_security_ids($channel_id, $ob_hash) {
|
||||||
|
|
||||||
|
$ret = [
|
||||||
|
'channel_id' => [],
|
||||||
|
'allow_cid' => [],
|
||||||
|
'allow_gid' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
if($channel_id) {
|
||||||
|
$ch = q("select channel_hash, portable_id from channel where channel_id = %d",
|
||||||
|
intval($channel_id)
|
||||||
|
);
|
||||||
|
if($ch) {
|
||||||
|
$ret['channel_id'][] = $ch[0]['channel_hash'];
|
||||||
|
$ret['channel_id'][] = $ch[0]['portable_id'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = [];
|
||||||
|
|
||||||
|
$x = q("select * from xchan where xchan_hash = '%s'",
|
||||||
|
dbesc($ob_hash)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($x) {
|
||||||
|
|
||||||
|
// include xchans for all zot-like networks
|
||||||
|
|
||||||
|
$xchans = q("select xchan_hash from xchan where xchan_hash = '%s' OR ( xchan_guid = '%s' AND xchan_pubkey = '%s' ) ",
|
||||||
|
dbesc($ob_hash),
|
||||||
|
dbesc($x[0]['xchan_guid']),
|
||||||
|
dbesc($x[0]['xchan_pubkey'])
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($xchans) {
|
||||||
|
$ret['allow_cid'] = ids_to_array($xchans,'xchan_hash');
|
||||||
|
$hashes = ids_to_querystr($xchans,'xchan_hash',true);
|
||||||
|
|
||||||
|
// private profiles are treated as a virtual group
|
||||||
|
|
||||||
|
$r = q("SELECT abook_profile from abook where abook_xchan in ( " . protect_sprintf($hashes) . " ) and abook_profile != '' ");
|
||||||
|
if($r) {
|
||||||
|
foreach ($r as $rv) {
|
||||||
|
$groups[] = 'vp.' . $rv['abook_profile'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// physical groups this identity is a member of
|
||||||
|
|
||||||
|
$r = q("SELECT hash FROM pgrp left join pgrp_member on pgrp.id = pgrp_member.gid WHERE xchan in ( " . protect_sprintf($hashes) . " ) ");
|
||||||
|
if($r) {
|
||||||
|
foreach ($r as $rv) {
|
||||||
|
$groups[] = $rv['hash'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$ret['allow_gid'] = $groups;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is used to determine which uid have posts which are visible to the logged in user (from the API) for the
|
// This is used to determine which uid have posts which are visible to the logged in user (from the API) for the
|
||||||
// public_timeline, and we can use this in a community page by making
|
// public_timeline, and we can use this in a community page by making
|
||||||
// $perms = (PERMS_NETWORK|PERMS_PUBLIC) unless logged in.
|
// $perms = (PERMS_NETWORK|PERMS_PUBLIC) unless logged in.
|
||||||
|
Reference in New Issue
Block a user