From 34d5efd7ef236640dd2ba6568bbf161e550c6bdf Mon Sep 17 00:00:00 2001 From: Koushik Dutta Date: Wed, 20 Apr 2022 09:33:29 -0700 Subject: [PATCH] rebroadcast: Fix rtsp client auth bugs --- common/src/rtsp-server.ts | 69 +++++++++++++---------- plugins/prebuffer-mixin/package-lock.json | 4 +- plugins/prebuffer-mixin/package.json | 2 +- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/common/src/rtsp-server.ts b/common/src/rtsp-server.ts index fbb662b45..43189b109 100644 --- a/common/src/rtsp-server.ts +++ b/common/src/rtsp-server.ts @@ -178,7 +178,7 @@ const quote = (str: string): string => `"${str.replace(/"/g, '\\"')}"`; export class RtspClient extends RtspBase { cseq = 0; session: string; - authorization: string; + wwwAuthenticate: string; requestTimeout: number; rfc4571 = new PassThrough(); needKeepAlive = false; @@ -224,9 +224,10 @@ export class RtspClient extends RtspBase { const line = `${method} ${fullUrl} RTSP/1.0`; const cseq = this.cseq++; headers['CSeq'] = cseq.toString(); + headers['User-Agent'] = 'Scrypted'; - if (this.authorization) - headers['Authorization'] = this.authorization; + if (this.wwwAuthenticate) + headers['Authorization'] = this.createAuthorizationHeader(method); if (this.session) headers['Session'] = this.session; @@ -285,6 +286,39 @@ export class RtspClient extends RtspBase { } } + createAuthorizationHeader(method: string) { + if (!this.wwwAuthenticate) + throw new Error('no WWW-Authenticate found'); + + const parsedUrl = new URL(this.url); + + if (this.wwwAuthenticate.includes('Basic')) { + const hash = BASIC.computeHash(parsedUrl); + return `Basic ${hash}`; + } + + const wwwAuth = DIGEST.parseWWWAuthenticateRest(this.wwwAuthenticate); + + const username = decodeURIComponent(parsedUrl.username); + const password = decodeURIComponent(parsedUrl.password); + + const ha1 = crypto.createHash('md5').update(`${username}:${wwwAuth.realm}:${password}`).digest('hex'); + const ha2 = crypto.createHash('md5').update(`${method}:${parsedUrl.pathname}`).digest('hex'); + const hash = crypto.createHash('md5').update(`${ha1}:${wwwAuth.nonce}:${ha2}`).digest('hex'); + + const params = { + username, + realm: wwwAuth.realm, + nonce: wwwAuth.nonce, + uri: parsedUrl.pathname, + algorithm: 'MD5', + response: hash, + }; + + const paramsString = Object.entries(params).map(([key, value]) => `${key}=${value && quote(value)}`).join(', '); + return `Digest ${paramsString}`; + } + async request(method: string, headers?: Headers, path?: string, body?: Buffer, authenticating?: boolean): Promise<{ headers: Headers, body: Buffer @@ -302,34 +336,7 @@ export class RtspClient extends RtspBase { if (authenticating) throw new Error('auth failed'); - const parsedUrl = new URL(this.url); - - if (wwwAuthenticate.includes('Basic')) { - const hash = BASIC.computeHash(parsedUrl); - this.authorization = `Basic ${hash}`; - } - else { - const wwwAuth = DIGEST.parseWWWAuthenticateRest(wwwAuthenticate); - - const username = decodeURIComponent(parsedUrl.username); - const password = decodeURIComponent(parsedUrl.password); - - const ha1 = crypto.createHash('md5').update(`${username}:${wwwAuth.realm}:${password}`).digest('hex'); - const ha2 = crypto.createHash('md5').update(`${method}:${parsedUrl.pathname}`).digest('hex'); - const hash = crypto.createHash('md5').update(`${ha1}:${wwwAuth.nonce}:${ha2}`).digest('hex'); - - const params = { - username, - realm: wwwAuth.realm, - nonce: wwwAuth.nonce, - uri: parsedUrl.pathname, - algorithm: 'MD5', - response: hash, - }; - - const paramsString = Object.entries(params).map(([key, value]) => `${key}=${value && quote(value)}`).join(', '); - this.authorization = `Digest ${paramsString}`; - } + this.wwwAuthenticate = wwwAuthenticate; return this.request(method, headers, path, body, true); } diff --git a/plugins/prebuffer-mixin/package-lock.json b/plugins/prebuffer-mixin/package-lock.json index e94b5972c..9444b238d 100644 --- a/plugins/prebuffer-mixin/package-lock.json +++ b/plugins/prebuffer-mixin/package-lock.json @@ -1,12 +1,12 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.237", + "version": "0.1.238", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.237", + "version": "0.1.238", "license": "Apache-2.0", "dependencies": { "@scrypted/common": "file:../../common", diff --git a/plugins/prebuffer-mixin/package.json b/plugins/prebuffer-mixin/package.json index b9ac8fd76..49b323658 100644 --- a/plugins/prebuffer-mixin/package.json +++ b/plugins/prebuffer-mixin/package.json @@ -1,6 +1,6 @@ { "name": "@scrypted/prebuffer-mixin", - "version": "0.1.237", + "version": "0.1.238", "description": "Rebroadcast and Prebuffer for VideoCameras.", "author": "Scrypted", "license": "Apache-2.0",