Add support for self-signed certificates

disco_info_form
Mickael Remond 5 years ago committed by Mickaël Rémond
parent 79803a8af9
commit 9577036327

@ -13,6 +13,32 @@ The goal is to make simple to write simple XMPP clients and components:
The library is designed to have minimal dependencies. For now, the library does not depend on any other library. The library is designed to have minimal dependencies. For now, the library does not depend on any other library.
## Configuration and connection
### Allowing Insecure TLS connection during development
It is not recommended to disable the check for domain name and certificate chain. Doing so would open your client
to man-in-the-middle attacks.
However, in development, XMPP servers often use self-signed certificates. In that situation, it is better to add the
root CA that signed the certificate to your trusted list of root CA. It avoids changing the code and limit the risk
of shipping an insecure client to production.
That said, if you really want to allow your client to trust any TLS certificate, you can customize Go standard
`tls.Config` and set it in Config struct.
Here is an example code to configure a client to allow connecting to a server with self-signed certificate. Note the
`InsecureSkipVerify` option. When using this `tls.Config` option, all the checks on the certificate are skipped.
```go
config := xmpp.Config{
Address: "localhost:5222",
Jid: "test@localhost",
Password: "test",
TLSConfig: tls.Config{InsecureSkipVerify: true},
}
```
## Supported specifications ## Supported specifications
### Clients ### Clients

@ -20,6 +20,7 @@ func main() {
Password: "test", Password: "test",
StreamLogger: os.Stdout, StreamLogger: os.Stdout,
Insecure: true, Insecure: true,
// TLSConfig: tls.Config{InsecureSkipVerify: true},
} }
router := xmpp.NewRouter() router := xmpp.NewRouter()

@ -86,8 +86,9 @@ func (c *ServerCheck) Check() error {
return fmt.Errorf("expecting starttls proceed: %s", err) return fmt.Errorf("expecting starttls proceed: %s", err)
} }
stanza.DefaultTlsConfig.ServerName = c.domain var tlsConfig tls.Config
tlsConn := tls.Client(tcpconn, &stanza.DefaultTlsConfig) tlsConfig.ServerName = c.domain
tlsConn := tls.Client(tcpconn, &tlsConfig)
// We convert existing connection to TLS // We convert existing connection to TLS
if err = tlsConn.Handshake(); err != nil { if err = tlsConn.Handshake(); err != nil {
return err return err

@ -1,6 +1,7 @@
package xmpp package xmpp
import ( import (
"crypto/tls"
"io" "io"
"os" "os"
) )
@ -13,6 +14,7 @@ type Config struct {
StreamLogger *os.File // Used for debugging StreamLogger *os.File // Used for debugging
Lang string // TODO: should default to 'en' Lang string // TODO: should default to 'en'
ConnectTimeout int // Client timeout in seconds. Default to 15 ConnectTimeout int // Client timeout in seconds. Default to 15
TLSConfig tls.Config
// Insecure can be set to true to allow to open a session without TLS. If TLS // Insecure can be set to true to allow to open a session without TLS. If TLS
// is supported on the server, we will still try to use it. // is supported on the server, we will still try to use it.
Insecure bool Insecure bool

@ -35,13 +35,15 @@ func NewSession(conn net.Conn, o Config) (net.Conn, *Session, error) {
// starttls // starttls
var tlsConn net.Conn var tlsConn net.Conn
tlsConn = s.startTlsIfSupported(conn, o.parsedJid.Domain) tlsConn = s.startTlsIfSupported(conn, o.parsedJid.Domain, o)
if s.TlsEnabled {
s.reset(conn, tlsConn, o)
}
if !s.TlsEnabled && !o.Insecure { if !s.TlsEnabled && !o.Insecure {
return nil, nil, NewConnError(errors.New("failed to negotiate TLS session"), true) err := fmt.Errorf("failed to negotiate TLS session : %s", s.err)
return nil, nil, NewConnError(err, true)
}
if s.TlsEnabled {
s.reset(conn, tlsConn, o)
} }
// auth // auth
@ -101,7 +103,7 @@ func (s *Session) open(domain string) (f stanza.StreamFeatures) {
return return
} }
func (s *Session) startTlsIfSupported(conn net.Conn, domain string) net.Conn { func (s *Session) startTlsIfSupported(conn net.Conn, domain string, o Config) net.Conn {
if s.err != nil { if s.err != nil {
return conn return conn
} }
@ -114,21 +116,30 @@ func (s *Session) startTlsIfSupported(conn net.Conn, domain string) net.Conn {
s.err = errors.New("expecting starttls proceed: " + s.err.Error()) s.err = errors.New("expecting starttls proceed: " + s.err.Error())
return conn return conn
} }
s.TlsEnabled = true
// TODO: add option to accept all TLS certificates: insecureSkipTlsVerify (DefaultTlsConfig.InsecureSkipVerify) o.TLSConfig.ServerName = domain
stanza.DefaultTlsConfig.ServerName = domain tlsConn := tls.Client(conn, &o.TLSConfig)
tlsConn := tls.Client(conn, &stanza.DefaultTlsConfig)
// We convert existing connection to TLS // We convert existing connection to TLS
if s.err = tlsConn.Handshake(); s.err != nil { if s.err = tlsConn.Handshake(); s.err != nil {
return tlsConn return tlsConn
} }
// We check that cert matches hostname if !o.TLSConfig.InsecureSkipVerify {
s.err = tlsConn.VerifyHostname(domain) // We check that cert matches hostname
s.err = tlsConn.VerifyHostname(domain)
}
if s.err == nil {
s.TlsEnabled = true
}
return tlsConn return tlsConn
} }
// If we do not allow cleartext connections, make it explicit that server do not support starttls
if !o.Insecure {
s.err = errors.New("XMPP server does not advertise support for starttls")
}
// starttls is not supported => we do not upgrade the connection: // starttls is not supported => we do not upgrade the connection:
return conn return conn
} }

@ -1,12 +1,9 @@
package stanza package stanza
import ( import (
"crypto/tls"
"encoding/xml" "encoding/xml"
) )
var DefaultTlsConfig tls.Config
// Used during stream initiation / session establishment // Used during stream initiation / session establishment
type TLSProceed struct { type TLSProceed struct {
XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"` XMLName xml.Name `xml:"urn:ietf:params:xml:ns:xmpp-tls proceed"`

@ -119,7 +119,7 @@ func (sm *StreamManager) connect() error {
var actualErr ConnError var actualErr ConnError
if xerrors.As(err, &actualErr) { if xerrors.As(err, &actualErr) {
if actualErr.Permanent { if actualErr.Permanent {
return xerrors.Errorf("unrecoverable connect error %w", actualErr) return xerrors.Errorf("unrecoverable connect error %#v", actualErr)
} }
} }
backoff.wait() backoff.wait()

Loading…
Cancel
Save