// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package xmpp implements the XMPP IM protocol, as specified in RFC 6120 and
// 6121.
package xmpp
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
"net"
"github.com/coyim/coyim/xmpp/interfaces"
)
var tlsVersionStrings = map[uint16]string{
tls.VersionSSL30: "SSL 3.0",
tls.VersionTLS10: "TLS 1.0",
tls.VersionTLS11: "TLS 1.1",
tls.VersionTLS12: "TLS 1.2",
}
var tlsCipherSuiteNames = map[uint16]string{
tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
}
func certName(cert *x509.Certificate) string {
name := cert.Subject
ret := ""
for _, org := range name.Organization {
ret += "O=" + org + "/"
}
for _, ou := range name.OrganizationalUnit {
ret += "OU=" + ou + "/"
}
if len(name.CommonName) > 0 {
ret += "CN=" + name.CommonName + "/"
}
return ret
}
// GetCipherSuiteName returns a human readable string of the cipher suite used in the state
func GetCipherSuiteName(tlsState tls.ConnectionState) string {
cipherSuite, ok := tlsCipherSuiteNames[tlsState.CipherSuite]
if !ok {
return "unknown"
}
return cipherSuite
}
// GetTLSVersion returns a human readable string of the TLS version used in the state
func GetTLSVersion(tlsState tls.ConnectionState) string {
version, ok := tlsVersionStrings[tlsState.Version]
if !ok {
return "unknown"
}
return version
}
func printTLSDetails(w io.Writer, tlsState tls.ConnectionState) {
fmt.Fprintf(w, " SSL/TLS version: %s\n", GetTLSVersion(tlsState))
fmt.Fprintf(w, " Cipher suite: %s\n", GetCipherSuiteName(tlsState))
}
// RFC 6120, section 5.4
func (d *dialer) negotiateSTARTTLS(c interfaces.Conn, conn net.Conn) error {
// RFC 6120, section 5.3
mandatoryToNegotiate := c.Features().StartTLS.Required.Local == "required"
if c.Config().SkipTLS && !mandatoryToNegotiate {
return nil
}
// Section 5.2 states:
// "Support for STARTTLS is REQUIRED in XMPP client and server implementations"
if c.Features().StartTLS.XMLName.Local == "" {
return errors.New("xmpp: server doesn't support TLS")
}
if err := d.startTLS(c, conn); err != nil {
return err
}
return c.SendInitialStreamHeader()
}
func (d *dialer) startTLS(c interfaces.Conn, conn net.Conn) error {
fmt.Fprintf(c.Out(), "", NsTLS)
proceed, err := nextStart(c.In())
if err != nil {
return err
}
if proceed.Name.Space != NsTLS || proceed.Name.Local != "proceed" {
return errors.New("xmpp: expected after but got <" + proceed.Name.Local + "> in " + proceed.Name.Space)
}
l := c.Config().GetLog()
io.WriteString(l, "Starting TLS handshake\n")
tlsConfig := c.Config().TLSConfig
if tlsConfig == nil {
tlsConfig = &tls.Config{}
}
tlsConfig.ServerName = c.OriginDomain()
tlsConfig.InsecureSkipVerify = true
tlsConn := d.tlsConnFactory(conn, tlsConfig)
if err := tlsConn.Handshake(); err != nil {
return err
}
tlsState := tlsConn.ConnectionState()
printTLSDetails(l, tlsState)
if err = d.verifier.Verify(tlsState, tlsConfig, c.OriginDomain()); err != nil {
return err
}
d.bindTransport(c, tlsConn)
return nil
}