From febdf7f7b612e177a88234e798ff69376db46a25 Mon Sep 17 00:00:00 2001 From: librelois <elois@ifee.fr> Date: Thu, 7 Dec 2017 18:24:32 +0100 Subject: [PATCH] [fix] ws2p heads : prevent fields injection & refactoring headsReceived --- app/lib/common-libs/constants.ts | 3 +- app/modules/ws2p/lib/WS2PCluster.ts | 82 ++++++++++++++++++----------- app/modules/ws2p/lib/constants.ts | 13 +++++ 3 files changed, 65 insertions(+), 33 deletions(-) diff --git a/app/lib/common-libs/constants.ts b/app/lib/common-libs/constants.ts index 907e52560..b34cb6278 100644 --- a/app/lib/common-libs/constants.ts +++ b/app/lib/common-libs/constants.ts @@ -83,7 +83,8 @@ export const CommonConstants = { SOFTWARE, SOFT_VERSION, POW_PREFIX, - ZERO_OR_POSITIVE_INT + ZERO_OR_POSITIVE_INT, + SIGNATURE }, BLOCK_GENERATED_VERSION: 10, diff --git a/app/modules/ws2p/lib/WS2PCluster.ts b/app/modules/ws2p/lib/WS2PCluster.ts index 36f64467f..e486d3a3d 100644 --- a/app/modules/ws2p/lib/WS2PCluster.ts +++ b/app/modules/ws2p/lib/WS2PCluster.ts @@ -127,28 +127,34 @@ export class WS2PCluster { async headsReceived(heads:WS2PHead[]) { await Promise.all(heads.map(async (h:WS2PHead) => { try { - if (h.messageV2) { + // HEAD v2 + if (h.messageV2 && h.messageV2.match(WS2PConstants.HEAD_V2_REGEXP)) { if (!h.sigV2) { throw "HEAD_MESSAGE_WRONGLY_SIGNED" - } - const [,,, pub, blockstamp, ws2pId,,,,,]:string[] = h.messageV2.split(':') - this.headReceived(h, pub, [pub, ws2pId].join('-'), blockstamp) - } else if (!h.message) { - throw "EMPTY_MESSAGE_FOR_HEAD" - } else if (!h.sig) { - throw "HEAD_MESSAGE_WRONGLY_SIGNED" - } else { - if (h.message.match(WS2PConstants.HEAD_V0_REGEXP)) { - const [,, pub, blockstamp]:string[] = h.message.split(':') - const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '00000000' + } else { + const [,,, pub, blockstamp, ws2pId,,,,,]:string[] = h.messageV2.split(':') this.headReceived(h, pub, [pub, ws2pId].join('-'), blockstamp) } - else if (h.message.match(WS2PConstants.HEAD_V1_REGEXP)) { + } + // HEAD v1 and HEAD v0 + else if (h.message && h.sig) { + if (h.message.match(WS2PConstants.HEAD_V1_REGEXP)) { const [,,, pub, blockstamp, ws2pId,,,]:string[] = h.message.split(':') - const fullId = await this.headReceived(h, pub, [pub, ws2pId].join('-'), blockstamp) + } else if (h.message.match(WS2PConstants.HEAD_V0_REGEXP)) { + const [,,pub, blockstamp]:string[] = h.message.split(':') + await this.headReceived(h, pub, [pub, "00000000"].join('-'), blockstamp) + } else { + throw "HEAD_WRONG_FORMAT" } } + else if (!h.message) { + throw "EMPTY_MESSAGE_FOR_HEAD" + } else if (!h.sig) { + throw "HEAD_MESSAGE_WRONGLY_SIGNED" + } else { + throw "HEAD_WRONG_FORMAT" + } } catch (e) { this.server.logger.trace(e) } @@ -171,29 +177,41 @@ export class WS2PCluster { }) } - private async headReceived(head:WS2PHead, pub:string, fullId:string, blockstamp:string) { + private async headReceived(h:WS2PHead, pub:string, fullId:string, blockstamp:string) { try { - const sigOK = verify(head.message, head.sig, pub) - const sigV2OK = (head.messageV2 !== undefined && head.sigV2 !== undefined) ? verify(head.messageV2, head.sigV2, pub):false - if ((sigV2OK && sigOK) || sigOK) { - // Already known or more recent or closer ? - const step = (this.headsCache[fullId]) ? this.headsCache[fullId].step || 0:0 - if (!this.headsCache[fullId] // unknow head - || parseInt(this.headsCache[fullId].blockstamp) < parseInt(blockstamp) // more recent head - || (head.step !== undefined && head.step < step && this.headsCache[fullId].blockstamp === blockstamp) // closer head - ) { - // Check that issuer is a member and that the block exists - const isAllowed = pub === this.server.conf.pair.pub || this.isConnectedKey(pub) || (await this.isMemberKey(pub)) - if (isAllowed) { - const exists = await this.existsBlock(blockstamp) - if (exists) { - this.headsCache[fullId] = { blockstamp, message: head.message, sig: head.sig, messageV2: head.messageV2, sigV2: head.sigV2, step: head.step } - this.newHeads.push(head) + // Prevent fields injection + if ( (h.message.match(WS2PConstants.HEAD_V1_REGEXP) || h.message.match(WS2PConstants.HEAD_V0_REGEXP)) + && h.sig.match(WS2PConstants.HEAD_SIG_REGEXP) + && (!h.messageV2 || h.messageV2.match(WS2PConstants.HEAD_V2_REGEXP)) + && (!h.sigV2 || h.sigV2.match(WS2PConstants.HEAD_SIG_REGEXP)) + && (!h.step || h.step.toFixed(0).match(/^[0-9]*$/)) + ) { + const head:WS2PHead = { message: h.message, sig: h.sig, messageV2: h.messageV2, sigV2: h.sigV2, step: h.step } + + const sigOK = verify(head.message, head.sig, pub) + const sigV2OK = (head.messageV2 !== undefined && head.sigV2 !== undefined) ? verify(head.messageV2, head.sigV2, pub):false + if ((sigV2OK && sigOK) || sigOK) { + // Already known or more recent or closer ? + const step = (this.headsCache[fullId]) ? this.headsCache[fullId].step || 0:0 + if (!this.headsCache[fullId] // unknow head + || parseInt(this.headsCache[fullId].blockstamp) < parseInt(blockstamp) // more recent head + || (head.step !== undefined && head.step < step && this.headsCache[fullId].blockstamp === blockstamp) // closer head + ) { + // Check that issuer is a member and that the block exists + const isAllowed = pub === this.server.conf.pair.pub || this.isConnectedKey(pub) || (await this.isMemberKey(pub)) + if (isAllowed) { + const exists = await this.existsBlock(blockstamp) + if (exists) { + this.headsCache[fullId] = { blockstamp, message: head.message, sig: head.sig, messageV2: head.messageV2, sigV2: head.sigV2, step: head.step } + this.newHeads.push(head) + } } } + } else { + throw "HEAD_MESSAGE_WRONGLY_SIGNED" } } else { - throw "HEAD_MESSAGE_WRONGLY_SIGNED" + throw "HEAD_WRONG_FORMAT" } } catch (e) { this.server.logger.trace(e) diff --git a/app/modules/ws2p/lib/constants.ts b/app/modules/ws2p/lib/constants.ts index a48d34646..b57598b7b 100644 --- a/app/modules/ws2p/lib/constants.ts +++ b/app/modules/ws2p/lib/constants.ts @@ -59,8 +59,21 @@ export const WS2PConstants = { + '(' + CommonConstants.FORMATS.SOFTWARE + '):' + '(' + CommonConstants.FORMATS.SOFT_VERSION + '):' + '(' + CommonConstants.FORMATS.POW_PREFIX + ')' + + '$'), + + HEAD_V2_REGEXP: new RegExp('^WS2P(?:O[CT][SAM])?(?:I[CT])?:HEAD:2:' + + '(' + CommonConstants.FORMATS.PUBKEY + '):' + + '(' + CommonConstants.FORMATS.BLOCKSTAMP + '):' + + '(' + CommonConstants.FORMATS.WS2PID + '):' + + '(' + CommonConstants.FORMATS.SOFTWARE + '):' + + '(' + CommonConstants.FORMATS.SOFT_VERSION + '):' + + '(' + CommonConstants.FORMATS.POW_PREFIX + '):' + + '(' + CommonConstants.FORMATS.ZERO_OR_POSITIVE_INT + '):' + + '(' + CommonConstants.FORMATS.ZERO_OR_POSITIVE_INT + ')' + '(?::' + CommonConstants.FORMATS.TIMESTAMP + ')?' + '$'), + + HEAD_SIG_REGEXP: new RegExp(CommonConstants.FORMATS.SIGNATURE), HOST_ONION_REGEX: CommonConstants.HOST_ONION_REGEX, FULL_ADDRESS_ONION_REGEX: CommonConstants.WS_FULL_ADDRESS_ONION_REGEX, -- GitLab