1// Copyright 2013 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package xmpp implements the XMPP IM protocol, as specified in RFC 6120 and
6// 6121.
7package xmpp
8
9import (
10	"crypto/tls"
11	"crypto/x509"
12	"errors"
13	"fmt"
14	"io"
15	"net"
16
17	"github.com/coyim/coyim/xmpp/interfaces"
18)
19
20var tlsVersionStrings = map[uint16]string{
21	tls.VersionSSL30: "SSL 3.0",
22	tls.VersionTLS10: "TLS 1.0",
23	tls.VersionTLS11: "TLS 1.1",
24	tls.VersionTLS12: "TLS 1.2",
25}
26
27var tlsCipherSuiteNames = map[uint16]string{
28	tls.TLS_RSA_WITH_RC4_128_SHA:                "TLS_RSA_WITH_RC4_128_SHA",
29	tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA:           "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
30	tls.TLS_RSA_WITH_AES_128_CBC_SHA:            "TLS_RSA_WITH_AES_128_CBC_SHA",
31	tls.TLS_RSA_WITH_AES_256_CBC_SHA:            "TLS_RSA_WITH_AES_256_CBC_SHA",
32	tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:        "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
33	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
34	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
35	tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA:          "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
36	tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:     "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
37	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:      "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
38	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:      "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
39	tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:   "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
40	tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
41}
42
43func certName(cert *x509.Certificate) string {
44	name := cert.Subject
45	ret := ""
46
47	for _, org := range name.Organization {
48		ret += "O=" + org + "/"
49	}
50	for _, ou := range name.OrganizationalUnit {
51		ret += "OU=" + ou + "/"
52	}
53	if len(name.CommonName) > 0 {
54		ret += "CN=" + name.CommonName + "/"
55	}
56	return ret
57}
58
59// GetCipherSuiteName returns a human readable string of the cipher suite used in the state
60func GetCipherSuiteName(tlsState tls.ConnectionState) string {
61	cipherSuite, ok := tlsCipherSuiteNames[tlsState.CipherSuite]
62	if !ok {
63		return "unknown"
64	}
65	return cipherSuite
66}
67
68// GetTLSVersion returns a human readable string of the TLS version used in the state
69func GetTLSVersion(tlsState tls.ConnectionState) string {
70	version, ok := tlsVersionStrings[tlsState.Version]
71	if !ok {
72		return "unknown"
73	}
74
75	return version
76}
77
78func printTLSDetails(w io.Writer, tlsState tls.ConnectionState) {
79	fmt.Fprintf(w, "  SSL/TLS version: %s\n", GetTLSVersion(tlsState))
80	fmt.Fprintf(w, "  Cipher suite: %s\n", GetCipherSuiteName(tlsState))
81}
82
83// RFC 6120, section 5.4
84func (d *dialer) negotiateSTARTTLS(c interfaces.Conn, conn net.Conn) error {
85	// RFC 6120, section 5.3
86	mandatoryToNegotiate := c.Features().StartTLS.Required.Local == "required"
87	if c.Config().SkipTLS && !mandatoryToNegotiate {
88		return nil
89	}
90
91	// Section 5.2 states:
92	// "Support for STARTTLS is REQUIRED in XMPP client and server implementations"
93	if c.Features().StartTLS.XMLName.Local == "" {
94		return errors.New("xmpp: server doesn't support TLS")
95	}
96
97	if err := d.startTLS(c, conn); err != nil {
98		return err
99	}
100
101	return c.SendInitialStreamHeader()
102}
103
104func (d *dialer) startTLS(c interfaces.Conn, conn net.Conn) error {
105	fmt.Fprintf(c.Out(), "<starttls xmlns='%s'/>", NsTLS)
106
107	proceed, err := nextStart(c.In())
108	if err != nil {
109		return err
110	}
111
112	if proceed.Name.Space != NsTLS || proceed.Name.Local != "proceed" {
113		return errors.New("xmpp: expected <proceed> after <starttls> but got <" + proceed.Name.Local + "> in " + proceed.Name.Space)
114	}
115
116	l := c.Config().GetLog()
117	io.WriteString(l, "Starting TLS handshake\n")
118
119	tlsConfig := c.Config().TLSConfig
120	if tlsConfig == nil {
121		tlsConfig = &tls.Config{}
122	}
123
124	tlsConfig.ServerName = c.OriginDomain()
125	tlsConfig.InsecureSkipVerify = true
126
127	tlsConn := d.tlsConnFactory(conn, tlsConfig)
128	if err := tlsConn.Handshake(); err != nil {
129		return err
130	}
131
132	tlsState := tlsConn.ConnectionState()
133	printTLSDetails(l, tlsState)
134
135	if err = d.verifier.Verify(tlsState, tlsConfig, c.OriginDomain()); err != nil {
136		return err
137	}
138
139	d.bindTransport(c, tlsConn)
140
141	return nil
142}
143