diff --git a/starttls/imap.go b/starttls/imap.go new file mode 100644 index 0000000..3136950 --- /dev/null +++ b/starttls/imap.go @@ -0,0 +1,67 @@ +/*- + * Copyright 2017 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package starttls + +import ( + "bufio" + "crypto/tls" + "fmt" + "net" +) + +func dumpAuthTLSFromIMAP(dialer Dialer, address string, config *tls.Config) (*tls.ConnectionState, error) { + c, err := dialer.Dial("tcp", address) + if err != nil { + return nil, err + } + + conn := c.(*net.TCPConn) + status, err := readIMAP(conn) + if err != nil { + return nil, err + } + if status != "OK" { + return nil, fmt.Errorf("IMAP server responded with %s, was expecting OK", status) + } + + fmt.Fprintf(conn, "1 STARTTLS\r\n") + status, err = readIMAP(conn) + if err != nil { + return nil, err + } + if status != "OK" { + return nil, fmt.Errorf("IMAP server responded with %s, was expecting OK", status) + } + + tlsConn := tls.Client(conn, config) + err = tlsConn.Handshake() + if err != nil { + return nil, err + } + + state := tlsConn.ConnectionState() + return &state, nil +} + +func readIMAP(conn *net.TCPConn) (string, error) { + reader := bufio.NewReader(conn) + response, err := reader.ReadString('\n') + if err != nil { + return "", err + } + return response[2:4], nil +} diff --git a/starttls/starttls.go b/starttls/starttls.go index 9c935ac..df5c7b1 100644 --- a/starttls/starttls.go +++ b/starttls/starttls.go @@ -35,7 +35,7 @@ import ( ) // Protocols are the names of supported protocols -var Protocols []string = []string{"mysql", "postgres", "psql", "smtp", "ldap", "ftp"} +var Protocols []string = []string{"mysql", "postgres", "psql", "smtp", "ldap", "ftp", "imap"} type connectResult struct { state *tls.ConnectionState @@ -234,6 +234,10 @@ func GetConnectionState(startTLSType, connectName, connectTo, identity, clientCe addr := withDefaultPort(connectTo, 21) state, err = dumpAuthTLSFromFTP(dialer, addr, tlsConfig) res <- connectResult{state, err} + case "imap": + addr := withDefaultPort(connectTo, 143) + state, err = dumpAuthTLSFromIMAP(dialer, addr, tlsConfig) + res <- connectResult{state, err} default: res <- connectResult{nil, fmt.Errorf("unknown StartTLS protocol: %s", startTLSType)} }