Merge pull request #138 from jpneverwas/external
Support SASL EXTERNAL and proxies for TLS connections
This commit is contained in:
		
						commit
						73e444401d
					
				
							
								
								
									
										52
									
								
								examples/simple-tor.go/simple-tor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								examples/simple-tor.go/simple-tor.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/thoj/go-ircevent"
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const addr = "libera75jm6of4wxpxt4aynol3xjmbtxgfyjpu34ss4d7r7q2v5zrpyd.onion:6697"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This demos connecting to Libera.Chat over TOR using SASL EXTERNAL and a TLS
 | 
				
			||||||
 | 
					// client cert. It assumes a TOR SOCKS service is running on localhost:9050
 | 
				
			||||||
 | 
					// and requires an existing account with a fingerprint already registered. See
 | 
				
			||||||
 | 
					// https://libera.chat/guides/connect#accessing-liberachat-via-tor for details.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Pass the full path to your cert and key on the command line like so:
 | 
				
			||||||
 | 
					// $ go run simple-tor.go my-nick my-cert.pem my-key.pem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						os.Setenv("ALL_PROXY", "socks5h://localhost:9050")
 | 
				
			||||||
 | 
						nick, certFile := os.Args[1], os.Args[2]
 | 
				
			||||||
 | 
						keyFile := certFile
 | 
				
			||||||
 | 
						if len(os.Args) == 4 {
 | 
				
			||||||
 | 
							keyFile = os.Args[3]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						clientCert, err := tls.LoadX509KeyPair(certFile, keyFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ircnick1 := nick
 | 
				
			||||||
 | 
						irccon := irc.IRC(ircnick1, nick)
 | 
				
			||||||
 | 
						irccon.VerboseCallbackHandler = true
 | 
				
			||||||
 | 
						irccon.UseSASL = true
 | 
				
			||||||
 | 
						irccon.SASLMech = "EXTERNAL"
 | 
				
			||||||
 | 
						irccon.Debug = true
 | 
				
			||||||
 | 
						irccon.UseTLS = true
 | 
				
			||||||
 | 
						irccon.TLSConfig = &tls.Config{
 | 
				
			||||||
 | 
							InsecureSkipVerify: true,
 | 
				
			||||||
 | 
							Certificates: []tls.Certificate{clientCert},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						irccon.AddCallback("001", func(e *irc.Event) {})
 | 
				
			||||||
 | 
						irccon.AddCallback("376", func(e *irc.Event) {
 | 
				
			||||||
 | 
							log.Println("Quitting")
 | 
				
			||||||
 | 
							irccon.Quit()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						err = irccon.Connect(addr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						irccon.Loop()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								go.mod
									
									
									
									
									
								
							@ -2,4 +2,7 @@ module github.com/thoj/go-ircevent
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
go 1.12
 | 
					go 1.12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require golang.org/x/text v0.3.2
 | 
					require (
 | 
				
			||||||
 | 
						golang.org/x/net v0.0.0-20210614182718-04defd469f4e
 | 
				
			||||||
 | 
						golang.org/x/text v0.3.6
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.sum
									
									
									
									
									
								
							@ -1,3 +1,10 @@
 | 
				
			|||||||
 | 
					golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
 | 
				
			||||||
 | 
					golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
					golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
				
			||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
					golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
				
			||||||
 | 
					golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 | 
				
			||||||
 | 
					golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								irc.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								irc.go
									
									
									
									
									
								
							@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/net/proxy"
 | 
				
			||||||
	"golang.org/x/text/encoding"
 | 
						"golang.org/x/text/encoding"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -461,15 +462,14 @@ func (irc *Connection) Connect(server string) error {
 | 
				
			|||||||
		return errors.New("empty 'user'")
 | 
							return errors.New("empty 'user'")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if irc.UseTLS {
 | 
						dialer := proxy.FromEnvironmentUsing(&net.Dialer{Timeout: irc.Timeout})
 | 
				
			||||||
		dialer := &net.Dialer{Timeout: irc.Timeout}
 | 
						irc.socket, err = dialer.Dial("tcp", irc.Server)
 | 
				
			||||||
		irc.socket, err = tls.DialWithDialer(dialer, "tcp", irc.Server, irc.TLSConfig)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		irc.socket, err = net.DialTimeout("tcp", irc.Server, irc.Timeout)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if irc.UseTLS {
 | 
				
			||||||
 | 
							irc.socket = tls.Client(irc.socket, irc.TLSConfig)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if irc.Encoding == nil {
 | 
						if irc.Encoding == nil {
 | 
				
			||||||
		irc.Encoding = encoding.Nop
 | 
							irc.Encoding = encoding.Nop
 | 
				
			||||||
 | 
				
			|||||||
@ -31,8 +31,8 @@ func (irc *Connection) setupSASLCallbacks(result chan<- *SASLResult) (callbacks
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if e.Arguments[1] == "ACK" && listContains(e.Arguments[2], "sasl") {
 | 
								if e.Arguments[1] == "ACK" && listContains(e.Arguments[2], "sasl") {
 | 
				
			||||||
				if irc.SASLMech != "PLAIN" {
 | 
									if irc.SASLMech != "PLAIN" && irc.SASLMech != "EXTERNAL" {
 | 
				
			||||||
					result <- &SASLResult{true, errors.New("only PLAIN is supported")}
 | 
										result <- &SASLResult{true, errors.New("only PLAIN and EXTERNAL supported")}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				irc.SendRaw("AUTHENTICATE " + irc.SASLMech)
 | 
									irc.SendRaw("AUTHENTICATE " + irc.SASLMech)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@ -41,6 +41,10 @@ func (irc *Connection) setupSASLCallbacks(result chan<- *SASLResult) (callbacks
 | 
				
			|||||||
	callbacks = append(callbacks, CallbackID{"CAP", id})
 | 
						callbacks = append(callbacks, CallbackID{"CAP", id})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	id = irc.AddCallback("AUTHENTICATE", func(e *Event) {
 | 
						id = irc.AddCallback("AUTHENTICATE", func(e *Event) {
 | 
				
			||||||
 | 
							if irc.SASLMech == "EXTERNAL" {
 | 
				
			||||||
 | 
								irc.SendRaw("AUTHENTICATE +")
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		str := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s\x00%s\x00%s", irc.SASLLogin, irc.SASLLogin, irc.SASLPassword)))
 | 
							str := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s\x00%s\x00%s", irc.SASLLogin, irc.SASLLogin, irc.SASLPassword)))
 | 
				
			||||||
		irc.SendRaw("AUTHENTICATE " + str)
 | 
							irc.SendRaw("AUTHENTICATE " + str)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
				
			|||||||
@ -41,3 +41,49 @@ func TestConnectionSASL(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	irccon.Loop()
 | 
						irccon.Loop()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 1. Register fingerprint with IRC network
 | 
				
			||||||
 | 
					// 2. Add SASLKeyPem="-----BEGIN PRIVATE KEY-----..."
 | 
				
			||||||
 | 
					//    and SASLCertPem="-----BEGIN CERTIFICATE-----..."
 | 
				
			||||||
 | 
					//    to CI environment as masked variables
 | 
				
			||||||
 | 
					func TestConnectionSASLExternal(t *testing.T) {
 | 
				
			||||||
 | 
						SASLServer := "irc.freenode.net:7000"
 | 
				
			||||||
 | 
						keyPem := os.Getenv("SASLKeyPem")
 | 
				
			||||||
 | 
						certPem := os.Getenv("SASLCertPem")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if certPem == "" || keyPem == "" {
 | 
				
			||||||
 | 
							t.Skip("Env vars SASLKeyPem SASLCertPem not present, skipping")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if testing.Short() {
 | 
				
			||||||
 | 
							t.Skip("skipping test in short mode.")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						cert, err := tls.X509KeyPair([]byte(certPem), []byte(keyPem))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("SASL EXTERNAL cert creation failed: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						irccon := IRC("go-eventirc", "go-eventirc")
 | 
				
			||||||
 | 
						irccon.VerboseCallbackHandler = true
 | 
				
			||||||
 | 
						irccon.Debug = true
 | 
				
			||||||
 | 
						irccon.UseTLS = true
 | 
				
			||||||
 | 
						irccon.UseSASL = true
 | 
				
			||||||
 | 
						irccon.SASLMech = "EXTERNAL"
 | 
				
			||||||
 | 
						irccon.TLSConfig = &tls.Config{
 | 
				
			||||||
 | 
							InsecureSkipVerify: true,
 | 
				
			||||||
 | 
							Certificates: []tls.Certificate{cert},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						irccon.AddCallback("366", func(e *Event) {
 | 
				
			||||||
 | 
							irccon.Privmsg("#go-eventirc", "Test Message SASL EXTERNAL\n")
 | 
				
			||||||
 | 
							time.Sleep(2 * time.Second)
 | 
				
			||||||
 | 
							irccon.Quit()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = irccon.Connect(SASLServer)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("SASL EXTERNAL failed: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						irccon.Loop()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user