mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-11-05 12:12:58 -05:00
ajax/stream.php sends stream commands (query, pause, seek, zoom, quit) to the zms whose unix socket matches a connkey, with no permission check. Connkeys are six random digits chosen when the stream starts, so any logged-in user who guessed or saw one could read the status of another user's stream, of a monitor they may not view included, and pause or end it. A second zms started with the same connkey also waited on the first one's lock and then took over its socket. zms already authenticates the user and checks their access to the monitor or event before it streams. Keep that user's id and put it in the lock, command socket and reply socket names (zms-<connkey>-<user id>s.sock); stream.php builds the names from the caller's own id through zmsSocketPath(), so a request reaches only the caller's streams. Without authentication the id is 0 on both sides. Adds a StreamBase test in tests/zm_stream.cpp and tests/php/test_zms_socket_path.php, which pin the same names. The existing command-socket-teardown test is updated for the owner-suffixed names (it postdates the reporter's base commit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
1.9 KiB
PHP
47 lines
1.9 KiB
PHP
<?php
|
|
// Security regression test: ajax/stream.php must address a zms by the names zms
|
|
// itself uses, which carry the id of the user zms authenticated. Before, the
|
|
// names held only the 6-digit connkey, so any logged-in user who guessed one
|
|
// could query, pause or quit another user's stream, of a monitor they may not
|
|
// view included.
|
|
//
|
|
// tests/zm_stream.cpp checks the C++ side (StreamBase::openComms()) against the
|
|
// same names.
|
|
//
|
|
// zmsSocketPath() is taken from functions.php, which can't be loaded on its own.
|
|
//
|
|
// Run as: php tests/php/test_zms_socket_path.php
|
|
|
|
$failures = 0;
|
|
$passes = 0;
|
|
|
|
function check($name, $got, $want) {
|
|
global $failures, $passes;
|
|
if ($got === $want) {
|
|
$passes++;
|
|
echo "ok - $name\n";
|
|
} else {
|
|
$failures++;
|
|
echo "FAIL - $name (got ".var_export($got, true).", want ".var_export($want, true).")\n";
|
|
}
|
|
}
|
|
|
|
$source = file_get_contents(__DIR__.'/../../web/includes/functions.php');
|
|
if (!preg_match('/^function zmsSocketPath\(.*?^}/ms', $source, $m)) {
|
|
echo "FAIL - zmsSocketPath() not found in functions.php\n";
|
|
exit(1);
|
|
}
|
|
define('ZM_PATH_SOCKS', '/run/zm');
|
|
eval($m[0]);
|
|
|
|
check('command socket of user 7', zmsSocketPath('004242', 7, 's.sock'), '/run/zm/zms-004242-7s.sock');
|
|
check('reply socket of user 7', zmsSocketPath(4242, '7', 'w.sock'), '/run/zm/zms-004242-7w.sock');
|
|
check('lock of user 7', zmsSocketPath(4242, 7, '.lock'), '/run/zm/zms-004242-7.lock');
|
|
check('another user gets another socket', zmsSocketPath(4242, 8, 's.sock') !== zmsSocketPath(4242, 7, 's.sock'), true);
|
|
check('no authentication: owner 0', zmsSocketPath(4242, 0, 's.sock'), '/run/zm/zms-004242-0s.sock');
|
|
check('the connkey stays numeric', zmsSocketPath('42/../x', 7, 's.sock'), '/run/zm/zms-000042-7s.sock');
|
|
check('the owner stays numeric', zmsSocketPath(4242, '7/../x', 's.sock'), '/run/zm/zms-004242-7s.sock');
|
|
|
|
echo "\n$passes passed, $failures failed\n";
|
|
exit($failures ? 1 : 0);
|