Add support for FAST authentication.

v0.10
Martin Dosch 2 months ago
parent 5ecfe200c1
commit 61f5ab705d
No known key found for this signature in database
GPG Key ID: 52A57CFCE13D657D

@ -6,6 +6,7 @@
### Added
- Add support for SASL2 and BIND2 (via go-xmpp > v0.1.5).
- Add support for FAST authentication (via go-xmpp > v0.1.5).
- Add a warning when run by the user *root*.
## [v0.9.0] 2024-03-28

@ -8,7 +8,8 @@ require (
github.com/gabriel-vasile/mimetype v1.4.3
github.com/google/uuid v1.6.0
github.com/pborman/getopt/v2 v2.1.0
github.com/xmppo/go-xmpp v0.1.6-0.20240409085338-7486b7a3638c
github.com/xmppo/go-xmpp v0.1.6-0.20240410131439-2f331ed19c57
golang.org/x/crypto v0.22.0
salsa.debian.org/mdosch/xmppsrv v0.2.6
)
@ -17,7 +18,6 @@ require (
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect

@ -27,10 +27,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/xmppo/go-xmpp v0.1.5 h1:Nxrf+NPxBcpmDTVSNpHPEdHccXJAHE3uMULnM0fOI1U=
github.com/xmppo/go-xmpp v0.1.5/go.mod h1:yOhaLKwPWIe/lMiDS6btyI8lCa56j8RD3iSrel0k/8c=
github.com/xmppo/go-xmpp v0.1.6-0.20240409085338-7486b7a3638c h1:NEaqrFrkYveuMPEOxOhArIvRm7NcXI4Bo4ef1iNSeJQ=
github.com/xmppo/go-xmpp v0.1.6-0.20240409085338-7486b7a3638c/go.mod h1:wR5vg1WrtcaP0DiW8XRUH9YLdFDjwlwJWl6zQEUqNuo=
github.com/xmppo/go-xmpp v0.1.6-0.20240410131439-2f331ed19c57 h1:B5xFeyo9+Cj37kLlWqyYtub8a/bdlfTV+SOKq5b3yeo=
github.com/xmppo/go-xmpp v0.1.6-0.20240410131439-2f331ed19c57/go.mod h1:wR5vg1WrtcaP0DiW8XRUH9YLdFDjwlwJWl6zQEUqNuo=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=

@ -6,7 +6,10 @@ package main
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/gob"
"errors"
"fmt"
"log"
@ -17,7 +20,9 @@ import (
"runtime"
"strings"
"github.com/google/uuid" // BSD-3-Clause
"github.com/google/uuid" // BSD-3-Clause
"github.com/xmppo/go-xmpp" // BSD-3-Clause
"golang.org/x/crypto/scrypt" // BSD-3-Clause
)
func validUTF8(s string) string {
@ -52,6 +57,98 @@ func readFile(path string) (*bytes.Buffer, error) {
return buffer, nil
}
func getFastData(jid string, password string) (xmpp.Fast, error) {
folder := strings.Replace(strings.Replace(jid, "@", "_at_", -1), ".", "_", -1)
var fast xmpp.Fast
fastPath, err := getDataPath(folder)
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to read fast cache file: %w", err)
}
fastFileLoc := fastPath + "fast"
buf, err := readFile(fastFileLoc)
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to read fast cache file: %w", err)
}
decBuf := bytes.NewBuffer(buf.Bytes())
decoder := gob.NewDecoder(decBuf)
err = decoder.Decode(&fast)
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to read fast cache file: %w", err)
}
salt := make([]byte, 32)
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to create aes key: %w", err)
}
c, err := aes.NewCipher([]byte(key))
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to read fast cache file: %w", err)
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to read fast cache file: %w", err)
}
nonceSize := gcm.NonceSize()
cryptBuf := []byte(fast.Token)
nonce, cryptBuf := cryptBuf[:nonceSize], cryptBuf[nonceSize:]
tokenBuf, err := gcm.Open(nil, []byte(nonce), cryptBuf, nil)
if err != nil {
return xmpp.Fast{}, fmt.Errorf("getFastData: failed to read fast cache file: %w", err)
}
fast.Token = string(tokenBuf)
return fast, nil
}
func writeFastData(jid string, password string, fast xmpp.Fast) error {
var encBuf bytes.Buffer
folder := strings.Replace(strings.Replace(jid, "@", "_at_", -1), ".", "_", -1)
fastPath, err := getDataPath(folder)
if err != nil {
return fmt.Errorf("writeFastData: failed to write fast cache file: %w", err)
}
fastFileLoc := fastPath + "fast"
salt := make([]byte, 32)
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
if err != nil {
return fmt.Errorf("writeFastData: failed to create aes cipher: %w", err)
}
c, err := aes.NewCipher(key)
if err != nil {
return fmt.Errorf("writeFastData: failed to create aes cipher: %w", err)
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return fmt.Errorf("writeFastData: failed to create aes cipher: %w", err)
}
nonce := make([]byte, gcm.NonceSize())
_, err = rand.Read(nonce)
if err != nil {
return fmt.Errorf("writeFastData: failed to create aes cipher: %w", err)
}
buf := gcm.Seal(nonce, nonce, []byte(fast.Token), nil)
fast.Token = string(buf)
encode := gob.NewEncoder(&encBuf)
err = encode.Encode(fast)
if err != nil {
return fmt.Errorf("writeFastData: failed to create fast token file: %w", err)
}
file, err := os.Create(fastFileLoc)
if err != nil {
return fmt.Errorf("writeFastData: failed to create fast token file: %w", err)
}
defer file.Close()
if runtime.GOOS != "windows" {
_ = file.Chmod(os.FileMode(defaultFileRights))
} else {
_ = file.Chmod(os.FileMode(defaultFileRightsWin))
}
_, err = file.Write(encBuf.Bytes())
if err != nil {
return fmt.Errorf("writeFastData: failed to write fast token file: %w", err)
}
return nil
}
func getClientID(jid string) (string, error) {
var clientID string
folder := strings.Replace(strings.Replace(jid, "@", "_at_", -1), ".", "_", -1)

@ -92,6 +92,7 @@ func main() {
message, user, server, password, alias string
oxPrivKey *crypto.Key
recipients []recipientsType
fast xmpp.Fast
)
// Define command line flags.
@ -268,6 +269,13 @@ func main() {
fmt.Println(err)
}
fast, _ = getFastData(user, password)
// Reset FAST token and mechanism if expired.
if time.Now().After(fast.Expiry) {
fast.Token = ""
fast.Mechanism = ""
}
// Use ALPN
var tlsConfig tls.Config
tlsConfig.ServerName = user[strings.Index(user, "@")+1:]
@ -288,12 +296,14 @@ func main() {
os.Exit(0)
}
resource := "go-sendxmpp." + getShortID()
// Set XMPP connection options.
options := xmpp.Options{
Host: server,
User: user,
DialTimeout: timeout,
Resource: "go-sendxmpp." + getShortID(),
Resource: resource,
Password: password,
// NoTLS doesn't mean that no TLS is used at all but that instead
// of using an encrypted connection to the server (direct TLS)
@ -301,14 +311,16 @@ func main() {
// set when NoTLS is set go-sendxmpp won't use unencrypted
// client-to-server connections.
// See https://pkg.go.dev/github.com/xmppo/go-xmpp#Options
NoTLS: !*flagDirectTLS,
StartTLS: !*flagDirectTLS,
Debug: *flagDebug,
TLSConfig: &tlsConfig,
Mechanism: *flagSCRAMPinning,
SSDP: !*flagSSDPOff,
UserAgentSW: "go-sendxmpp",
UserAgentID: clientID,
NoTLS: !*flagDirectTLS,
StartTLS: !*flagDirectTLS,
Debug: *flagDebug,
TLSConfig: &tlsConfig,
Mechanism: *flagSCRAMPinning,
SSDP: !*flagSSDPOff,
UserAgentSW: resource,
UserAgentID: clientID,
FastToken: fast.Token,
FastMechanism: fast.Mechanism,
}
// Read message from file.
@ -356,6 +368,17 @@ func main() {
log.Fatal(err)
}
// Store fast token if a new one is received.
if client.Fast.Token != "" && client.Fast.Token != fast.Token {
fast.Token = client.Fast.Token
fast.Mechanism = client.Fast.Mechanism
fast.Expiry = client.Fast.Expiry
err := writeFastData(user, password, fast)
if err != nil {
fmt.Println(err)
}
}
iqc := make(chan xmpp.IQ, defaultBufferSize)
msgc := make(chan xmpp.Chat, defaultBufferSize)
go rcvStanzas(client, iqc, msgc)

Loading…
Cancel
Save