feat(xep): Allow inline enablement of carbons
This commit is contained in:
parent
ce1815d1f3
commit
ec6b5ab753
@ -10,3 +10,4 @@ const startTlsNegotiator = 'im.moxxmpp.core.starttls';
|
|||||||
const sasl2Negotiator = 'org.moxxmpp.sasl.sasl2';
|
const sasl2Negotiator = 'org.moxxmpp.sasl.sasl2';
|
||||||
const bind2Negotiator = 'org.moxxmpp.bind2';
|
const bind2Negotiator = 'org.moxxmpp.bind2';
|
||||||
const saslFASTNegotiator = 'org.moxxmpp.sasl.fast';
|
const saslFASTNegotiator = 'org.moxxmpp.sasl.fast';
|
||||||
|
const carbonsNegotiator = 'org.moxxmpp.bind2.carbons';
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:moxxmpp/src/connection.dart';
|
import 'package:moxxmpp/src/connection.dart';
|
||||||
import 'package:moxxmpp/src/events.dart';
|
import 'package:moxxmpp/src/events.dart';
|
||||||
@ -7,10 +8,15 @@ import 'package:moxxmpp/src/managers/data.dart';
|
|||||||
import 'package:moxxmpp/src/managers/handlers.dart';
|
import 'package:moxxmpp/src/managers/handlers.dart';
|
||||||
import 'package:moxxmpp/src/managers/namespaces.dart';
|
import 'package:moxxmpp/src/managers/namespaces.dart';
|
||||||
import 'package:moxxmpp/src/namespaces.dart';
|
import 'package:moxxmpp/src/namespaces.dart';
|
||||||
|
import 'package:moxxmpp/src/negotiators/namespaces.dart';
|
||||||
|
import 'package:moxxmpp/src/negotiators/negotiator.dart';
|
||||||
|
import 'package:moxxmpp/src/negotiators/sasl2.dart';
|
||||||
import 'package:moxxmpp/src/stanza.dart';
|
import 'package:moxxmpp/src/stanza.dart';
|
||||||
import 'package:moxxmpp/src/stringxml.dart';
|
import 'package:moxxmpp/src/stringxml.dart';
|
||||||
|
import 'package:moxxmpp/src/types/result.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
import 'package:moxxmpp/src/xeps/xep_0030/xep_0030.dart';
|
||||||
import 'package:moxxmpp/src/xeps/xep_0297.dart';
|
import 'package:moxxmpp/src/xeps/xep_0297.dart';
|
||||||
|
import 'package:moxxmpp/src/xeps/xep_0386.dart';
|
||||||
|
|
||||||
/// This manager class implements support for XEP-0280.
|
/// This manager class implements support for XEP-0280.
|
||||||
class CarbonsManager extends XmppManagerBase {
|
class CarbonsManager extends XmppManagerBase {
|
||||||
@ -173,6 +179,16 @@ class CarbonsManager extends XmppManagerBase {
|
|||||||
_isEnabled = true;
|
_isEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
void setEnabled() {
|
||||||
|
_isEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@internal
|
||||||
|
void setDisabled() {
|
||||||
|
_isEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if a carbon sent by [senderJid] is valid to prevent vulnerabilities like
|
/// Checks if a carbon sent by [senderJid] is valid to prevent vulnerabilities like
|
||||||
/// the ones listed at https://xmpp.org/extensions/xep-0280.html#security.
|
/// the ones listed at https://xmpp.org/extensions/xep-0280.html#security.
|
||||||
///
|
///
|
||||||
@ -185,3 +201,82 @@ class CarbonsManager extends XmppManagerBase {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CarbonsNegotiator extends Sasl2FeatureNegotiator
|
||||||
|
implements Bind2FeatureNegotiator {
|
||||||
|
CarbonsNegotiator() : super(0, false, carbonsXmlns, carbonsNegotiator);
|
||||||
|
|
||||||
|
/// Flag indicating whether we requested to enable carbons inline (true) or not
|
||||||
|
/// (false).
|
||||||
|
bool _requestedEnablement = false;
|
||||||
|
|
||||||
|
/// Logger
|
||||||
|
final Logger _log = Logger('CarbonsNegotiator');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool canInlineFeature(List<XMLNode> features) => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<XMLNode>> onSasl2FeaturesReceived(XMLNode sasl2Features) async {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<bool, NegotiatorError>> onSasl2Success(XMLNode response) async {
|
||||||
|
if (_requestedEnablement) {
|
||||||
|
final enabled = response
|
||||||
|
.firstTag('bound', xmlns: bind2Xmlns)
|
||||||
|
?.firstTag('enabled', xmlns: carbonsXmlns);
|
||||||
|
final cm = attributes.getManagerById<CarbonsManager>(carbonsManager)!;
|
||||||
|
if (enabled != null) {
|
||||||
|
_log.finest('Successfully enabled Message Carbons inline');
|
||||||
|
cm.setEnabled();
|
||||||
|
} else {
|
||||||
|
_log.warning('Failed to enable Message Carbons inline');
|
||||||
|
cm.setDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return const Result(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Result<NegotiatorState, NegotiatorError>> negotiate(
|
||||||
|
XMLNode nonza,
|
||||||
|
) async {
|
||||||
|
return const Result(NegotiatorState.done);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<XMLNode>> onBind2FeaturesReceived(
|
||||||
|
List<String> bind2Features) async {
|
||||||
|
if (!bind2Features.contains(carbonsXmlns)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
_requestedEnablement = true;
|
||||||
|
return [
|
||||||
|
XMLNode.xmlns(
|
||||||
|
tag: 'enable',
|
||||||
|
xmlns: carbonsXmlns,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> postRegisterCallback() async {
|
||||||
|
attributes
|
||||||
|
.getNegotiatorById<Sasl2Negotiator>(sasl2Negotiator)
|
||||||
|
?.registerNegotiator(this);
|
||||||
|
attributes
|
||||||
|
.getNegotiatorById<Bind2Negotiator>(bind2Negotiator)
|
||||||
|
?.registerNegotiator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void reset() {
|
||||||
|
_requestedEnablement = false;
|
||||||
|
|
||||||
|
super.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import 'package:moxxmpp/moxxmpp.dart';
|
import 'package:moxxmpp/moxxmpp.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
import '../helpers/logging.dart';
|
||||||
import '../helpers/xmpp.dart';
|
import '../helpers/xmpp.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
initLogger();
|
||||||
|
|
||||||
test("Test if we're vulnerable against CVE-2020-26547 style vulnerabilities",
|
test("Test if we're vulnerable against CVE-2020-26547 style vulnerabilities",
|
||||||
() async {
|
() async {
|
||||||
final attributes = XmppManagerAttributes(
|
final attributes = XmppManagerAttributes(
|
||||||
@ -52,4 +55,92 @@ void main() {
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Test enabling message carbons inline with Bind2', () async {
|
||||||
|
final fakeSocket = StubTCPSocket([
|
||||||
|
StringExpectation(
|
||||||
|
"<stream:stream xmlns='jabber:client' version='1.0' xmlns:stream='http://etherx.jabber.org/streams' to='test.server' xml:lang='en'>",
|
||||||
|
'''
|
||||||
|
<stream:stream
|
||||||
|
xmlns="jabber:client"
|
||||||
|
version="1.0"
|
||||||
|
xmlns:stream="http://etherx.jabber.org/streams"
|
||||||
|
from="test.server"
|
||||||
|
xml:lang="en">
|
||||||
|
<stream:features xmlns="http://etherx.jabber.org/streams">
|
||||||
|
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
|
||||||
|
<mechanism>PLAIN</mechanism>
|
||||||
|
</mechanisms>
|
||||||
|
<authentication xmlns='urn:xmpp:sasl:2'>
|
||||||
|
<mechanism>PLAIN</mechanism>
|
||||||
|
<inline>
|
||||||
|
<resume xmlns="urn:xmpp:sm:3" />
|
||||||
|
<bind xmlns="urn:xmpp:bind:0">
|
||||||
|
<inline>
|
||||||
|
<feature var="urn:xmpp:sm:3" />
|
||||||
|
<feature var="urn:xmpp:carbons:2" />
|
||||||
|
</inline>
|
||||||
|
</bind>
|
||||||
|
</inline>
|
||||||
|
</authentication>
|
||||||
|
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
|
||||||
|
<required/>
|
||||||
|
</bind>
|
||||||
|
</stream:features>''',
|
||||||
|
),
|
||||||
|
StanzaExpectation(
|
||||||
|
"<authenticate xmlns='urn:xmpp:sasl:2' mechanism='PLAIN'><user-agent id='d4565fa7-4d72-4749-b3d3-740edbf87770'><software>moxxmpp</software><device>PapaTutuWawa's awesome device</device></user-agent><initial-response>AHBvbHlub21kaXZpc2lvbgBhYWFh</initial-response><bind xmlns='urn:xmpp:bind:0'><enable xmlns='urn:xmpp:carbons:2' /></bind></authenticate>",
|
||||||
|
'''
|
||||||
|
<success xmlns='urn:xmpp:sasl:2'>
|
||||||
|
<authorization-identifier>polynomdivision@test.server/test-resource</authorization-identifier>
|
||||||
|
<bound xmlns='urn:xmpp:bind:0'>
|
||||||
|
<enabled xmlns='urn:xmpp:carbons:2' />
|
||||||
|
</bound>
|
||||||
|
</success>
|
||||||
|
''',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
final conn = XmppConnection(
|
||||||
|
TestingReconnectionPolicy(),
|
||||||
|
AlwaysConnectedConnectivityManager(),
|
||||||
|
fakeSocket,
|
||||||
|
)
|
||||||
|
..setConnectionSettings(
|
||||||
|
ConnectionSettings(
|
||||||
|
jid: JID.fromString('polynomdivision@test.server'),
|
||||||
|
password: 'aaaa',
|
||||||
|
useDirectTLS: true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..setResource('test-resource', triggerEvent: false);
|
||||||
|
await conn.registerManagers([
|
||||||
|
RosterManager(TestingRosterStateManager('', [])),
|
||||||
|
DiscoManager([]),
|
||||||
|
CarbonsManager(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await conn.registerFeatureNegotiators([
|
||||||
|
SaslPlainNegotiator(),
|
||||||
|
ResourceBindingNegotiator(),
|
||||||
|
CarbonsNegotiator(),
|
||||||
|
Bind2Negotiator(),
|
||||||
|
Sasl2Negotiator(
|
||||||
|
userAgent: const UserAgent(
|
||||||
|
id: 'd4565fa7-4d72-4749-b3d3-740edbf87770',
|
||||||
|
software: 'moxxmpp',
|
||||||
|
device: "PapaTutuWawa's awesome device",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final result = await conn.connect(
|
||||||
|
waitUntilLogin: true,
|
||||||
|
shouldReconnect: false,
|
||||||
|
enableReconnectOnSuccess: false,
|
||||||
|
);
|
||||||
|
expect(result.isType<NegotiatorError>(), false);
|
||||||
|
expect(conn.resource, 'test-resource');
|
||||||
|
expect(
|
||||||
|
conn.getManagerById<CarbonsManager>(carbonsManager)!.isEnabled, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user