Skip to content

Commit

Permalink
Support MIT-MAGIC-COOKIE-1 authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
robert-ancell committed Nov 28, 2023
1 parent b89c20a commit 6f43f54
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 28 deletions.
101 changes: 101 additions & 0 deletions lib/src/x11_authority_file.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import 'dart:convert';
import 'dart:io';

enum X11AuthorityAddressType { unknown, IPv4, IPv6, local, wild }

class X11AuthorityAddress {
final X11AuthorityAddressType type;
final List<int> address;

X11AuthorityAddress(this.type, this.address);

@override
String toString() => 'X11AuthorityAddress($type, $address)';
}

class X11AuthorityFileRecord {
final X11AuthorityAddress address;
final String display;
final String authorizationName;
final List<int> authorizationData;

X11AuthorityFileRecord(
{required this.address,
required this.display,
required this.authorizationName,
required this.authorizationData});

@override
String toString() =>
"X11AuthorityFileRecord($address, '$display', '$authorizationName', $authorizationData)";
}

class X11AuthorityFile {
final List<X11AuthorityFileRecord> records;

X11AuthorityFile(this.records);
}

class X11AuthorityFileLoader {
var _data = <int>[];
var _offset = 0;

Future<X11AuthorityFile> load(String path) async {
var f = File(path);
_data = await f.readAsBytes();
_offset = 0;

var records = <X11AuthorityFileRecord>[];
while (_offset < _data.length) {
var family = _readUint16();
var address = _readBytes();
var display = _readString();
var authorizationName = _readString();
var authorizationData = _readBytes();

var addressType = {
0: X11AuthorityAddressType.IPv4,
6: X11AuthorityAddressType.IPv6,
256: X11AuthorityAddressType.local,
65535: X11AuthorityAddressType.wild
}[family] ??
X11AuthorityAddressType.unknown;

var record = X11AuthorityFileRecord(
address: X11AuthorityAddress(addressType, address),
display: display,
authorizationName: authorizationName,
authorizationData: authorizationData);
records.add(record);
}

return X11AuthorityFile(records);
}

int _readUint8() {
if (_offset >= _data.length) {
throw 'Invalid XAuthority file';
}
var value = _data[_offset];
_offset++;
return value;
}

int _readUint16() {
return _readUint8() << 8 | _readUint8();
}

List<int> _readBytes() {
var length = _readUint16();
var value = <int>[];
for (var i = 0; i < length; i++) {
value.add(_readUint8());
}
return value;
}

String _readString() {
var data = _readBytes();
return utf8.decode(data);
}
}
36 changes: 33 additions & 3 deletions lib/src/x11_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'x11_authority_file.dart';
import 'x11_big_requests.dart';
import 'x11_composite.dart';
import 'x11_damage.dart';
Expand Down Expand Up @@ -289,11 +290,38 @@ class X11Client {
throw "Invalid DISPLAY: '$display'";
}

await connectToHost(host, displayNumber);
var authorityPath = Platform.environment['XAUTHORITY'];
if (authorityPath == null) {
var home = Platform.environment['HOME'];
if (home == null) {
throw 'Unable to determine HOME';
}

authorityPath = '$home/.Xauthority';
}
var authorityFile = await X11AuthorityFileLoader().load(authorityPath);
var records = authorityFile.records.where((r) =>
r.address.type == X11AuthorityAddressType.local &&
r.authorizationName == 'MIT-MAGIC-COOKIE-1');
var authorizationName = '';
var authorizationData = <int>[];
if (records.isNotEmpty) {
var record = records.first;
authorizationName = 'MIT-MAGIC-COOKIE-1';
authorizationData = record.authorizationData;
}

await connectToHost(host,
displayNumber: displayNumber,
authorizationName: authorizationName,
authorizationData: authorizationData);
}

/// Connects to the X server on [host] using [displayNumber].
Future<void> connectToHost(String host, int displayNumber) async {
Future<void> connectToHost(String host,
{int displayNumber = 0,
String authorizationName = '',
List<int> authorizationData = const []}) async {
if (!(host == '' || host == 'localhost')) {
throw 'Connecting to host $host not supported';
}
Expand All @@ -305,7 +333,9 @@ class X11Client {

var buffer = X11WriteBuffer();
buffer.writeUint8(0x6c); // Little endian
var request = X11SetupRequest();
var request = X11SetupRequest(
authorizationName: authorizationName,
authorizationData: authorizationData);
request.encode(buffer);
_socket?.add(buffer.data);

Expand Down
48 changes: 23 additions & 25 deletions lib/src/x11_requests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,52 @@ int pad(int length) {

class X11SetupRequest {
final X11Version protocolVersion;
final String authorizationProtocolName;
final List<int> authorizationProtocolData;
final String authorizationName;
final List<int> authorizationData;

const X11SetupRequest(
{this.protocolVersion = const X11Version(11, 0),
this.authorizationProtocolName = '',
this.authorizationProtocolData = const []});
this.authorizationName = '',
this.authorizationData = const []});

factory X11SetupRequest.fromBuffer(X11ReadBuffer buffer) {
buffer.skip(1);
var protocolMajorVersion = buffer.readUint16();
var protocolMinorVersion = buffer.readUint16();
var authorizationProtocolNameLength = buffer.readUint16();
var authorizationProtocolDataLength = buffer.readUint16();
var authorizationProtocolName =
buffer.readString8(authorizationProtocolNameLength);
buffer.skip(pad(authorizationProtocolNameLength));
var authorizationProtocolData = <int>[];
for (var i = 0; i < authorizationProtocolDataLength; i++) {
authorizationProtocolData.add(buffer.readUint8());
}
buffer.skip(pad(authorizationProtocolDataLength));
var authorizationNameLength = buffer.readUint16();
var authorizationDataLength = buffer.readUint16();
var authorizationName = buffer.readString8(authorizationNameLength);
buffer.skip(pad(authorizationNameLength));
var authorizationData = <int>[];
for (var i = 0; i < authorizationDataLength; i++) {
authorizationData.add(buffer.readUint8());
}
buffer.skip(pad(authorizationDataLength));
buffer.skip(2);

return X11SetupRequest(
protocolVersion: X11Version(protocolMajorVersion, protocolMinorVersion),
authorizationProtocolName: authorizationProtocolName,
authorizationProtocolData: authorizationProtocolData);
authorizationName: authorizationName,
authorizationData: authorizationData);
}

void encode(X11WriteBuffer buffer) {
buffer.skip(1);
buffer.writeUint16(protocolVersion.major);
buffer.writeUint16(protocolVersion.minor);
var authorizationProtocolNameLength =
buffer.getString8Length(authorizationProtocolName);
buffer.writeUint16(authorizationProtocolNameLength);
buffer.writeUint16(authorizationProtocolData.length);
var authorizationNameLength = buffer.getString8Length(authorizationName);
buffer.writeUint16(authorizationNameLength);
buffer.writeUint16(authorizationData.length);
buffer.skip(2);
buffer.writeString8(authorizationProtocolName);
buffer.skip(pad(authorizationProtocolNameLength));
buffer.writeListOfUint8(authorizationProtocolData);
buffer.skip(pad(authorizationProtocolData.length));
buffer.writeString8(authorizationName);
buffer.skip(pad(authorizationNameLength));
buffer.writeListOfUint8(authorizationData);
buffer.skip(pad(authorizationData.length));
}

@override
String toString() =>
"X11SetupRequest(protocolVersion = $protocolVersion, authorizationProtocolName: '$authorizationProtocolName', authorizationProtocolData: $authorizationProtocolData)";
"X11SetupRequest(protocolVersion = $protocolVersion, authorizationName: '$authorizationName', authorizationData: $authorizationData)";
}

class X11SetupFailedReply {
Expand Down

0 comments on commit 6f43f54

Please sign in to comment.