mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-09-07 08:05:27 -05:00
NAS: Use specialized TLS connection instead of piping data
This commit is contained in:
@@ -23,12 +23,6 @@ type nasInConn struct {
|
||||
in io.Reader
|
||||
}
|
||||
|
||||
type nasIOConn struct {
|
||||
net.Conn
|
||||
in io.Reader
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
func (c nasInConn) Read(b []byte) (int, error) { return c.in.Read(b) }
|
||||
|
||||
func (c nasInConn) Close() error {
|
||||
@@ -39,21 +33,6 @@ func (c nasInConn) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c nasIOConn) Read(b []byte) (int, error) { return c.in.Read(b) }
|
||||
|
||||
func (c nasIOConn) Write(b []byte) (int, error) { return c.out.Write(b) }
|
||||
|
||||
func (c nasIOConn) Close() error {
|
||||
// Don't actually close the underlying connection here
|
||||
if closer, ok := c.in.(io.Closer); ok {
|
||||
_ = closer.Close()
|
||||
}
|
||||
if closer, ok := c.out.(io.Closer); ok {
|
||||
_ = closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *httpListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
@@ -103,25 +82,23 @@ func (l *tlsListener) Accept() (net.Conn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We're gonna need like 3 pipes here, from client -> tls -> filter -> server
|
||||
readFromServer, writeFromServer := io.Pipe()
|
||||
readToServer, writeToServer := io.Pipe()
|
||||
readToFilter, writeToFilter := io.Pipe()
|
||||
tlsConn := tlsConnection{
|
||||
Conn: conn,
|
||||
}
|
||||
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
r := bufio.NewReader(readToFilter)
|
||||
if err := filterDuplicateHost(writeToServer, r); err != nil {
|
||||
_ = writeToServer.CloseWithError(err)
|
||||
r := bufio.NewReader(&tlsConn)
|
||||
if err := filterDuplicateHost(pw, r); err != nil {
|
||||
_ = pw.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
_, err := io.Copy(writeToServer, r)
|
||||
_ = writeToServer.CloseWithError(err)
|
||||
_, err := io.Copy(pw, r)
|
||||
_ = pw.CloseWithError(err)
|
||||
}()
|
||||
go handleIncomingTLS(conn, readFromServer, writeToFilter)
|
||||
return &nasIOConn{
|
||||
Conn: conn,
|
||||
in: readToServer,
|
||||
out: writeFromServer,
|
||||
return &nasInConn{
|
||||
Conn: &tlsConn,
|
||||
in: pr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
158
nas/tls.go
158
nas/tls.go
@@ -30,7 +30,7 @@ import (
|
||||
// See here: https://github.com/shutterbug2000/wii-ssl-bug
|
||||
// https://github.com/KaeruTeam/nds-constraint
|
||||
|
||||
// Don't use this for anything else, it's not secure
|
||||
// Don't use this TLS implementation for anything else, it's not secure
|
||||
|
||||
var (
|
||||
rsaKeyWii *rsa.PrivateKey
|
||||
@@ -226,43 +226,39 @@ func setupExploitDS(config common.Config) {
|
||||
}...)
|
||||
}
|
||||
|
||||
// handleIncomingTLS handles incoming requests from the listener.
|
||||
func handleIncomingTLS(rawConn net.Conn, fromServer *io.PipeReader, toServer *io.PipeWriter) {
|
||||
moduleName := "NAS-TLS:" + rawConn.RemoteAddr().String()
|
||||
|
||||
var err error
|
||||
closed := false
|
||||
if rsaKeyWii == nil && rsaKeyDS == nil {
|
||||
// Only handle real TLS requests
|
||||
err, closed = proxyRealTLS(rawConn, fromServer, toServer)
|
||||
} else {
|
||||
err, closed = handleTLS(moduleName, rawConn, fromServer, toServer)
|
||||
}
|
||||
|
||||
toServer.CloseWithError(err)
|
||||
if !closed {
|
||||
_ = rawConn.Close()
|
||||
}
|
||||
type tlsConnection struct {
|
||||
net.Conn
|
||||
handshake bool
|
||||
tlsConn io.ReadWriter
|
||||
}
|
||||
|
||||
var errUnexpectedBytes = errors.New("unexpected bytes")
|
||||
|
||||
func readBytes(r io.Reader, expected []byte, consumed []byte) ([]byte, error) {
|
||||
index := len(consumed)
|
||||
buf := make([]byte, len(expected))
|
||||
copy(buf, consumed)
|
||||
|
||||
for index < len(expected) {
|
||||
n, err := r.Read(buf[index:])
|
||||
if err != nil {
|
||||
return buf[:index+n], err
|
||||
func (c *tlsConnection) Read(b []byte) (int, error) {
|
||||
if !c.handshake {
|
||||
if err := c.handleTLSHandshake(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !bytes.Equal(buf[index:index+n], expected[index:index+n]) {
|
||||
return buf[:index+n], errUnexpectedBytes
|
||||
}
|
||||
index += n
|
||||
}
|
||||
return buf, nil
|
||||
|
||||
return c.tlsConn.Read(b)
|
||||
}
|
||||
|
||||
func (c *tlsConnection) Write(b []byte) (int, error) {
|
||||
if !c.handshake {
|
||||
if err := c.handleTLSHandshake(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.tlsConn.Write(b)
|
||||
}
|
||||
|
||||
func (c *tlsConnection) Close() error {
|
||||
if c.tlsConn != nil {
|
||||
if tlsConn, ok := c.tlsConn.(*tls.Conn); ok {
|
||||
return tlsConn.Close()
|
||||
}
|
||||
}
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -276,31 +272,39 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// handleTLS handles the TLS request from the Wii or the DS. It may call handleRealTLS if the request is from a modern web browser.
|
||||
func handleTLS(moduleName string, conn net.Conn, fromServer *io.PipeReader, toServer *io.PipeWriter) (error, bool) {
|
||||
// Recover from panics
|
||||
// handleTLSHandshake handles the TLS request from the Wii or the DS, and creates tlsConn for further communication.
|
||||
// It may call handleRealTLS if the request is from a modern web browser.
|
||||
func (c *tlsConnection) handleTLSHandshake() error {
|
||||
moduleName := "NAS-TLS:" + c.Conn.RemoteAddr().String()
|
||||
|
||||
// Recover from panics. TODO: is this really necessary?
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logging.Error(moduleName, "Panic:", r)
|
||||
}
|
||||
}()
|
||||
|
||||
if rsaKeyDS == nil && rsaKeyWii == nil {
|
||||
// Only handle real TLS connections
|
||||
return c.handleRealTLSHandshake(c.Conn)
|
||||
}
|
||||
|
||||
// Read client hello
|
||||
var peekMatchWii, peekMatchDS bool
|
||||
consumed := []byte{}
|
||||
var err error
|
||||
if rsaKeyDS != nil {
|
||||
consumed, err = readBytes(conn, dsClientHelloPrefix, consumed)
|
||||
consumed, err = readBytes(c.Conn, dsClientHelloPrefix, consumed)
|
||||
peekMatchDS = err == nil
|
||||
}
|
||||
|
||||
if rsaKeyWii != nil && err == errUnexpectedBytes {
|
||||
consumed, err = readBytes(conn, wiiClientHelloPrefix, consumed)
|
||||
consumed, err = readBytes(c.Conn, wiiClientHelloPrefix, consumed)
|
||||
peekMatchWii = err == nil
|
||||
}
|
||||
|
||||
if err != nil && err != errUnexpectedBytes {
|
||||
return err, false
|
||||
return err
|
||||
}
|
||||
|
||||
var macFn macFunction
|
||||
@@ -308,58 +312,53 @@ func handleTLS(moduleName string, conn net.Conn, fromServer *io.PipeReader, toSe
|
||||
var version uint16
|
||||
switch {
|
||||
case peekMatchWii:
|
||||
macFn, cipher, clientCipher, err = handleWiiTLSHandshake(moduleName, conn)
|
||||
macFn, cipher, clientCipher, err = handleWiiTLSHandshake(moduleName, c.Conn)
|
||||
version = VersionTLS10
|
||||
case peekMatchDS:
|
||||
macFn, cipher, clientCipher, err = handleDSSSLHandshake(moduleName, conn)
|
||||
macFn, cipher, clientCipher, err = handleDSSSLHandshake(moduleName, c.Conn)
|
||||
version = VersionSSL30
|
||||
|
||||
case err == errUnexpectedBytes:
|
||||
return proxyRealTLS(nasInConn{
|
||||
Conn: conn,
|
||||
in: io.MultiReader(bytes.NewReader(consumed), conn),
|
||||
}, fromServer, toServer)
|
||||
return c.handleRealTLSHandshake(nasInConn{
|
||||
Conn: c.Conn,
|
||||
in: io.MultiReader(bytes.NewReader(consumed), c.Conn),
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err, false
|
||||
return err
|
||||
}
|
||||
if macFn == nil || cipher == nil || clientCipher == nil {
|
||||
return errors.New("invalid TLS handshake result"), false
|
||||
return errors.New("invalid TLS handshake result")
|
||||
}
|
||||
|
||||
tlsConn := &consoleTLSConn{
|
||||
Conn: conn,
|
||||
c.tlsConn = &consoleTLSConn{
|
||||
Conn: c.Conn,
|
||||
MacFn: macFn,
|
||||
Cipher: cipher,
|
||||
ClientCipher: clientCipher,
|
||||
Version: version,
|
||||
}
|
||||
return tlsConn.proxyConsoleTLS(fromServer, toServer)
|
||||
c.handshake = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// proxyRealTLS handles the TLS request legitimately using crypto/tls
|
||||
func proxyRealTLS(rawConn net.Conn, fromServer *io.PipeReader, toServer *io.PipeWriter) (err error, closed bool) {
|
||||
defer toServer.CloseWithError(err)
|
||||
|
||||
// handleRealTLSHandshake handles the TLS request handshake legitimately using crypto/tls, and creates tlsConn
|
||||
func (c *tlsConnection) handleRealTLSHandshake(rawConn net.Conn) error {
|
||||
if realTLSConfig == nil {
|
||||
return errors.New("realTLSConfig is not set"), false
|
||||
return errors.New("realTLSConfig is not set")
|
||||
}
|
||||
|
||||
tlsConn := tls.Server(rawConn, realTLSConfig)
|
||||
|
||||
err = tlsConn.Handshake()
|
||||
err := tlsConn.Handshake()
|
||||
if err != nil {
|
||||
_ = tlsConn.Close()
|
||||
return err, false
|
||||
return err
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, err = io.Copy(tlsConn, fromServer)
|
||||
_ = tlsConn.Close()
|
||||
}()
|
||||
_, err = io.Copy(toServer, tlsConn)
|
||||
return err, true
|
||||
c.tlsConn = tlsConn
|
||||
c.handshake = true
|
||||
return nil
|
||||
}
|
||||
|
||||
type consoleTLSConn struct {
|
||||
@@ -434,15 +433,6 @@ func (c *consoleTLSConn) Write(b []byte) (n int, err error) {
|
||||
return c.Conn.Write(record)
|
||||
}
|
||||
|
||||
func (c *consoleTLSConn) proxyConsoleTLS(fromServer *io.PipeReader, toServer *io.PipeWriter) (err error, closed bool) {
|
||||
go func() {
|
||||
_, err = io.Copy(c, fromServer)
|
||||
_ = c.Close()
|
||||
}()
|
||||
_, err = io.Copy(toServer, c)
|
||||
return err, true
|
||||
}
|
||||
|
||||
func handleWiiTLSHandshake(moduleName string, conn io.ReadWriter) (macFn macFunction, cipher *rc4.Cipher, clientCipher *rc4.Cipher, err error) {
|
||||
clientHello := append(wiiClientHelloPrefix, make([]byte, 0x2-len(wiiClientHelloPrefix))...)
|
||||
_, err = io.ReadFull(conn, clientHello[len(wiiClientHelloPrefix):])
|
||||
@@ -767,6 +757,26 @@ func handleDSSSLHandshake(moduleName string, conn io.ReadWriter) (macFn macFunct
|
||||
return
|
||||
}
|
||||
|
||||
var errUnexpectedBytes = errors.New("unexpected bytes")
|
||||
|
||||
func readBytes(r io.Reader, expected []byte, consumed []byte) ([]byte, error) {
|
||||
index := len(consumed)
|
||||
buf := make([]byte, len(expected))
|
||||
copy(buf, consumed)
|
||||
|
||||
for index < len(expected) {
|
||||
n, err := r.Read(buf[index:])
|
||||
if err != nil {
|
||||
return buf[:index+n], err
|
||||
}
|
||||
if !bytes.Equal(buf[index:index+n], expected[index:index+n]) {
|
||||
return buf[:index+n], errUnexpectedBytes
|
||||
}
|
||||
index += n
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// The following functions are modified from the crypto standard library
|
||||
//
|
||||
// Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Reference in New Issue
Block a user