зеркало из https://github.com/nextcloud/spreed.git
Fix validation of requests from recording server
The credentials to be used are specific to the recording server and unrelated to the SIP bridge. The code follows the same schema used for validation of external signaling server requests in SignalingController. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Родитель
d16d5fbd1f
Коммит
ef42841772
|
@ -45,10 +45,10 @@
|
|||
|
||||
* Header:
|
||||
|
||||
| field | type | Description |
|
||||
| ------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `TALK_SIPBRIDGE_RANDOM` | string | Random string that needs to be concatenated with room token to generate the checksum using the `sip_bridge_shared_secret`. |
|
||||
| `TALK_SIPBRIDGE_CHECKSUM` | string | The checksum generated with `TALK_SIPBRIDGE_RANDOM`. |
|
||||
| field | type | Description |
|
||||
| ------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `TALK_RECORDING_RANDOM` | string | Random string that needs to be concatenated with room token to generate the checksum using the `recording_servers['secret']`. |
|
||||
| `TALK_RECORDING_CHECKSUM` | string | The checksum generated with `TALK_RECORDING_RANDOM`. |
|
||||
|
||||
* Data:
|
||||
|
||||
|
@ -68,7 +68,7 @@
|
|||
+ `400 Bad Request` Error: `owner_participant`: Owner is not to be a participant of room
|
||||
+ `400 Bad Request` Error: `owner_invalid`: Owner invalid
|
||||
+ `400 Bad Request` Error: `owner_permission`: Owner have not permission to store record file
|
||||
+ `401 Unauthorized` When the validation as SIP bridge failed
|
||||
+ `401 Unauthorized` When the validation as recording server failed
|
||||
+ `404 Not Found` Room not found
|
||||
+ `429 Too Many Request` Brute force protection
|
||||
|
||||
|
|
|
@ -28,22 +28,21 @@ namespace OCA\Talk\Controller;
|
|||
use InvalidArgumentException;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use OCA\Talk\Config;
|
||||
use OCA\Talk\Exceptions\UnauthorizedException;
|
||||
use OCA\Talk\Service\RecordingService;
|
||||
use OCA\Talk\Service\SIPBridgeService;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\IRequest;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class RecordingController extends AEnvironmentAwareController {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
private Config $talkConfig,
|
||||
private SIPBridgeService $SIPBridgeService,
|
||||
private IClientService $clientService,
|
||||
private RecordingService $recordingService
|
||||
private RecordingService $recordingService,
|
||||
private LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
@ -82,6 +81,33 @@ class RecordingController extends AEnvironmentAwareController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request is coming from an allowed backend.
|
||||
*
|
||||
* The backends are sending the custom header "Talk-Recording-Random"
|
||||
* containing at least 32 bytes random data, and the header
|
||||
* "Talk-Recording-Checksum", which is the SHA256-HMAC of the random data
|
||||
* and the body of the request, calculated with the shared secret from the
|
||||
* configuration.
|
||||
*
|
||||
* @param string $data
|
||||
* @return bool
|
||||
*/
|
||||
private function validateBackendRequest(string $data): bool {
|
||||
$random = $this->request->getHeader('Talk-Recording-Random');
|
||||
if (empty($random) || strlen($random) < 32) {
|
||||
$this->logger->debug("Missing random");
|
||||
return false;
|
||||
}
|
||||
$checksum = $this->request->getHeader('Talk-Recording-Checksum');
|
||||
if (empty($checksum)) {
|
||||
$this->logger->debug("Missing checksum");
|
||||
return false;
|
||||
}
|
||||
$hash = hash_hmac('sha256', $random . $data, $this->talkConfig->getRecordingSecret());
|
||||
return hash_equals($hash, strtolower($checksum));
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @RequireModeratorParticipant
|
||||
|
@ -111,20 +137,20 @@ class RecordingController extends AEnvironmentAwareController {
|
|||
/**
|
||||
* @PublicPage
|
||||
* @RequireRoom
|
||||
* @BruteForceProtection(action=talkSipBridgeSecret)
|
||||
* @BruteForceProtection(action=talkRecordingSecret)
|
||||
*
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function store(string $owner): DataResponse {
|
||||
try {
|
||||
$random = $this->request->getHeader('TALK_SIPBRIDGE_RANDOM');
|
||||
$checksum = $this->request->getHeader('TALK_SIPBRIDGE_CHECKSUM');
|
||||
$secret = $this->talkConfig->getSIPSharedSecret();
|
||||
if (!$this->SIPBridgeService->validateSIPBridgeRequest($random, $checksum, $secret, $this->room->getToken())) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
} catch (UnauthorizedException $e) {
|
||||
$response = new DataResponse([], Http::STATUS_UNAUTHORIZED);
|
||||
$data = $this->room->getToken();
|
||||
if (!$this->validateBackendRequest($data)) {
|
||||
$response = new DataResponse([
|
||||
'type' => 'error',
|
||||
'error' => [
|
||||
'code' => 'invalid_request',
|
||||
'message' => 'The request could not be authenticated.',
|
||||
],
|
||||
], Http::STATUS_UNAUTHORIZED);
|
||||
$response->throttle();
|
||||
return $response;
|
||||
}
|
||||
|
|
|
@ -3223,13 +3223,13 @@ class FeatureContext implements Context, SnippetAcceptingContext {
|
|||
public function userStoreRecordingFileInRoom(string $user, string $file, string $identifier, int $statusCode, string $apiVersion = 'v1'): void {
|
||||
$this->setCurrentUser($user);
|
||||
|
||||
$sipBridgeSharedSecret = 'the secret';
|
||||
$this->setAppConfig('spreed', new TableNode([['sip_bridge_shared_secret', $sipBridgeSharedSecret]]));
|
||||
$recordingServerSharedSecret = 'the secret';
|
||||
$this->setAppConfig('spreed', new TableNode([['recording_servers', json_encode(['secret' => $recordingServerSharedSecret])]]));
|
||||
$validRandom = md5((string) rand());
|
||||
$validChecksum = hash_hmac('sha256', $validRandom . self::$identifierToToken[$identifier], $sipBridgeSharedSecret);
|
||||
$validChecksum = hash_hmac('sha256', $validRandom . self::$identifierToToken[$identifier], $recordingServerSharedSecret);
|
||||
$headers = [
|
||||
'TALK_SIPBRIDGE_RANDOM' => $validRandom,
|
||||
'TALK_SIPBRIDGE_CHECKSUM' => $validChecksum,
|
||||
'TALK_RECORDING_RANDOM' => $validRandom,
|
||||
'TALK_RECORDING_CHECKSUM' => $validChecksum,
|
||||
];
|
||||
$options = [
|
||||
'multipart' => [
|
||||
|
|
Загрузка…
Ссылка в новой задаче