/// <summary> /// Find an entry which is most matched to the display number and the connection type. /// </summary> /// <param name="xauthFile">path to the .Xauthority file</param> /// <param name="display">display number</param> /// <returns>best-matched entry if it was found. otherwise null.</returns> public XauthorityEntry FindBest(string xauthFile, int display) { // from Xauth.h const ushort FamilyWild = 65535; string hostName = Dns.GetHostEntry("").HostName; // get fully qualified hostname XauthorityEntry candidate = null; foreach (var entry in ReadEntries(xauthFile)) { if (entry.Number != display) { continue; } if (entry.Family == FamilyWild) { candidate = entry; } if (entry.Address == hostName) { return(entry); // best } } return(candidate); }
/// <summary> /// Obtain a new cookie from the X server for the "Untrusted" access. /// </summary> /// <remarks> /// This method will be called in the setup procedure of this manager. /// </remarks> /// <param name="socketFactory"></param> /// <param name="display">display number</param> /// <param name="entry"></param> /// <returns>tuple of { authorization-id, authorization-data } if the untrusted access is available. otherwise null.</returns> private Tuple<uint, byte[]> GetUntrustedAccessCookie(Func<IX11Socket> socketFactory, int display, XauthorityEntry entry) { using (IX11Socket socket = socketFactory()) { socket.Connect(display); const bool BIGENDIAN = true; byte[] recv = new byte[100]; var xmsg = new XProtocolMessage(BIGENDIAN); var reader = new XDataReader(BIGENDIAN); // Note: if USE_UNTRUSTED_ACCESS was disabled, this method only checks the connectability to the X server. // initiation { byte[] authName = Encoding.ASCII.GetBytes(entry.Name); xmsg.Clear() .AppendByte(0x42) // MSB first .AppendByte(0) // unused .AppendUInt16(11) // protocol-major-version .AppendUInt16(0) // protocol-minor-version .AppendUInt16((ushort)authName.Length) // length of authorization-protocol-name .AppendUInt16((ushort)entry.Data.Length) // length of authorization-protocol-data .AppendUInt16(0) // unused .AppendBytes(authName) // authorization-protocol-name .AppendPaddingBytesOf(authName) .AppendBytes(entry.Data) // authorization-protocol-data .AppendPaddingBytesOf(entry.Data); if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 8, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } if (recv[0] != 1 /*Success*/) { throw new X11UtilException(Strings.GetString("X11AuthorizationFailed")); } int extraDataLen = reader.ReadUInt16(recv, 6) * 4; byte[] extraData = new byte[extraDataLen]; if (!socket.ReceiveBytes(extraData, 0, extraDataLen, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } } #if USE_UNTRUSTED_ACCESS // QueryExtension { byte[] extName = Encoding.ASCII.GetBytes("SECURITY"); xmsg.Clear() .AppendByte(98) // opcode .AppendByte(0) // unused .AppendUInt16((ushort)(2 + (extName.Length + 3) / 4)) // request-length .AppendUInt16((ushort)extName.Length) // length of name .AppendUInt16(0) // unused .AppendBytes(extName) // name .AppendPaddingBytesOf(extName); if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 32, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } if (recv[0] != 1 /*Reply*/ || recv[8] != 1 /*present*/) { // no SECURITY extension _protocolEventManager.Trace("[X11] X server doesn't have the SECURITY extension."); return null; } } byte secOpcode = recv[9]; // major-opcode of the SECURITY extension // SecurityQueryVersion { xmsg.Clear() .AppendByte(secOpcode) // major-opcode .AppendByte(0) // minor-opcode .AppendUInt16(2) // request-length .AppendUInt16(1) // client-major-version .AppendUInt16(0); // client-minor-version if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 32, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } } // SecurityGenerateAuthorization { byte[] authName = Encoding.ASCII.GetBytes(DEFAULT_AUTH_NAME); byte[] authData = new byte[0]; uint[] valueList = new uint[] { // timeout 0, // no timeout // trust-level 1, // SecurityClientUntrusted }; xmsg.Clear() .AppendByte(secOpcode) // major-opcode .AppendByte(1) // minor-opcode .AppendUInt16((ushort)(3 + (authName.Length + 3) / 4 + (authData.Length + 3) / 4 + valueList.Length)) .AppendUInt16((ushort)authName.Length) .AppendUInt16((ushort)authData.Length) .AppendUInt32(3) // value-mask : timeout + trust-level .AppendBytes(authName) .AppendPaddingBytesOf(authName) .AppendBytes(authData) .AppendPaddingBytesOf(authData) .AppendUInt32(valueList[0]) .AppendUInt32(valueList[1]); if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 32, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } if (recv[0] != 1 /*Reply*/) { return null; } uint authId = reader.ReadUInt32(recv, 8); int extraDataLength = (int)reader.ReadUInt32(recv, 4) * 4; if (extraDataLength < 0 || extraDataLength > 1024) { // something wrong... return null; } int datLength = (int)reader.ReadUInt16(recv, 12); if (datLength < 0 || datLength > extraDataLength) { // something wrong... return null; } byte[] extraData = new byte[extraDataLength]; if (!socket.ReceiveBytes(extraData, 0, extraDataLength, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } byte[] generatedAuthData = reader.ReadBytes(extraData, 0, datLength); return Tuple.Create(authId, generatedAuthData); } #else // USE_UNTRUSTED_ACCESS // no untrusted access return null; #endif // USE_UNTRUSTED_ACCESS } }
/// <summary> /// Obtain a new cookie from the X server for the "Untrusted" access. /// </summary> /// <remarks> /// This method will be called in the setup procedure of this manager. /// </remarks> /// <param name="socketFactory"></param> /// <param name="display">display number</param> /// <param name="entry"></param> /// <returns>tuple of { authorization-id, authorization-data } if the untrusted access is available. otherwise null.</returns> private Tuple <uint, byte[]> GetUntrustedAccessCookie(Func <IX11Socket> socketFactory, int display, XauthorityEntry entry) { using (IX11Socket socket = socketFactory()) { socket.Connect(display); const bool BIGENDIAN = true; byte[] recv = new byte[100]; var xmsg = new XProtocolMessage(BIGENDIAN); var reader = new XDataReader(BIGENDIAN); // Note: if USE_UNTRUSTED_ACCESS was disabled, this method only checks the connectability to the X server. // initiation { byte[] authName = Encoding.ASCII.GetBytes(entry.Name); xmsg.Clear() .AppendByte(0x42) // MSB first .AppendByte(0) // unused .AppendUInt16(11) // protocol-major-version .AppendUInt16(0) // protocol-minor-version .AppendUInt16((ushort)authName.Length) // length of authorization-protocol-name .AppendUInt16((ushort)entry.Data.Length) // length of authorization-protocol-data .AppendUInt16(0) // unused .AppendBytes(authName) // authorization-protocol-name .AppendPaddingBytesOf(authName) .AppendBytes(entry.Data) // authorization-protocol-data .AppendPaddingBytesOf(entry.Data); if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 8, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } if (recv[0] != 1 /*Success*/) { throw new X11UtilException(Strings.GetString("X11AuthorizationFailed")); } int extraDataLen = reader.ReadUInt16(recv, 6) * 4; byte[] extraData = new byte[extraDataLen]; if (!socket.ReceiveBytes(extraData, 0, extraDataLen, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } } #if USE_UNTRUSTED_ACCESS // QueryExtension { byte[] extName = Encoding.ASCII.GetBytes("SECURITY"); xmsg.Clear() .AppendByte(98) // opcode .AppendByte(0) // unused .AppendUInt16((ushort)(2 + (extName.Length + 3) / 4)) // request-length .AppendUInt16((ushort)extName.Length) // length of name .AppendUInt16(0) // unused .AppendBytes(extName) // name .AppendPaddingBytesOf(extName); if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 32, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } if (recv[0] != 1 /*Reply*/ || recv[8] != 1 /*present*/) { // no SECURITY extension _protocolEventManager.Trace("[X11] X server doesn't have the SECURITY extension."); return(null); } } byte secOpcode = recv[9]; // major-opcode of the SECURITY extension // SecurityQueryVersion { xmsg.Clear() .AppendByte(secOpcode) // major-opcode .AppendByte(0) // minor-opcode .AppendUInt16(2) // request-length .AppendUInt16(1) // client-major-version .AppendUInt16(0); // client-minor-version if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 32, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } } // SecurityGenerateAuthorization { byte[] authName = Encoding.ASCII.GetBytes(DEFAULT_AUTH_NAME); byte[] authData = new byte[0]; uint[] valueList = new uint[] { // timeout 0, // no timeout // trust-level 1, // SecurityClientUntrusted }; xmsg.Clear() .AppendByte(secOpcode) // major-opcode .AppendByte(1) // minor-opcode .AppendUInt16((ushort)(3 + (authName.Length + 3) / 4 + (authData.Length + 3) / 4 + valueList.Length)) .AppendUInt16((ushort)authName.Length) .AppendUInt16((ushort)authData.Length) .AppendUInt32(3) // value-mask : timeout + trust-level .AppendBytes(authName) .AppendPaddingBytesOf(authName) .AppendBytes(authData) .AppendPaddingBytesOf(authData) .AppendUInt32(valueList[0]) .AppendUInt32(valueList[1]); if (!socket.Send(xmsg.AsDataFragment(), SEND_TIMEOUT)) { throw new X11UtilException(Strings.GetString("FailedToSendMessageToXServer")); } if (!socket.ReceiveBytes(recv, 0, 32, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } if (recv[0] != 1 /*Reply*/) { return(null); } uint authId = reader.ReadUInt32(recv, 8); int extraDataLength = (int)reader.ReadUInt32(recv, 4) * 4; if (extraDataLength < 0 || extraDataLength > 1024) { // something wrong... return(null); } int datLength = (int)reader.ReadUInt16(recv, 12); if (datLength < 0 || datLength > extraDataLength) { // something wrong... return(null); } byte[] extraData = new byte[extraDataLength]; if (!socket.ReceiveBytes(extraData, 0, extraDataLength, RESPONSE_TIMEOUT)) { throw new X11UtilException(Strings.GetString("XServerDoesntRespond")); } byte[] generatedAuthData = reader.ReadBytes(extraData, 0, datLength); return(Tuple.Create(authId, generatedAuthData)); } #else // USE_UNTRUSTED_ACCESS // no untrusted access return(null); #endif // USE_UNTRUSTED_ACCESS } }
/// <summary> /// Setup the connection manager according to the parameters. /// </summary> /// <param name="param">parameters</param> /// <exception cref="X11UtilException"></exception> /// <exception cref="X11SocketException"></exception> public void Setup(X11ForwardingParams param) { if (_setupDone) { return; } _param = param.Clone(); _spoofedAuthProtocolName = DEFAULT_AUTH_NAME; _spoofedAuthCookie = GenerateCookie(); if (param.UseCygwinUnixDomainSocket) { _protocolEventManager.Trace("[X11] Use Cygwin's domain socket"); _socketFactory = () => new X11CygwinDomainSocket(param.X11UnixFolder); } else { _protocolEventManager.Trace("[X11] Use TCP socket"); _socketFactory = () => new X11TcpSocket(); } XauthorityEntry xauthEntry; if (param.NeedAuth) { string xauthFile = FindXauthorityFile(param); if (xauthFile == null) { throw new X11UtilException(Strings.GetString("XauthorityFileNotFound")); } var parser = new XauthorityParser(); xauthEntry = parser.FindBest(xauthFile, param.Display); if (xauthEntry == null) { throw new X11UtilException(Strings.GetString("SuitableAuthorizationInformationNotFound")); } } else { xauthEntry = new XauthorityEntry(0, "", param.Display, "", new byte[0]); } var cookieInfo = GetUntrustedAccessCookie(_socketFactory, param.Display, xauthEntry); if (cookieInfo == null) { // no SECURITY extension _protocolEventManager.Trace("[X11] \"Trusted\" access will be used."); _xAuthProtocolName = xauthEntry.Name; _xAuthCookie = xauthEntry.Data; _authEntry = xauthEntry; _authId = null; _setupDone = true; return; } _protocolEventManager.Trace("[X11] \"Untrusted\" access will be used."); _xAuthProtocolName = DEFAULT_AUTH_NAME; _xAuthCookie = cookieInfo.Item2; _authEntry = xauthEntry; _authId = cookieInfo.Item1; _setupDone = true; // TODO: // the authorization cookie should be deleted from the X server when // the forwarding channel is closed. }