about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorDrew DeVault <sir@cmpwn.com>2018-01-31 21:54:52 -0500
committerDrew DeVault <sir@cmpwn.com>2018-01-31 21:54:52 -0500
commit3139148c7b9ad6ed4fb9cd8cd3e4160a9b9ee46f (patch)
tree1bb500628bae5f6b64167e498b25ff09649a977c
parenta21afdaa6bab8f6d05bbe9272700eef571548a59 (diff)
downloadaerc-3139148c7b9ad6ed4fb9cd8cd3e4160a9b9ee46f.tar.gz
Add certificate approval flow
-rw-r--r--ui/account.go25
-rw-r--r--worker/imap/worker.go81
-rw-r--r--worker/types/messages.go26
3 files changed, 91 insertions, 41 deletions
diff --git a/ui/account.go b/ui/account.go
index 59544d4..0cd61c8 100644
--- a/ui/account.go
+++ b/ui/account.go
@@ -5,8 +5,6 @@ import (
 
 	tb "github.com/nsf/termbox-go"
 
-	"github.com/davecgh/go-spew/spew"
-
 	"git.sr.ht/~sircmpwn/aerc2/config"
 	"git.sr.ht/~sircmpwn/aerc2/worker"
 	"git.sr.ht/~sircmpwn/aerc2/worker/types"
@@ -64,12 +62,25 @@ func (acc *AccountTab) GetChannel() chan types.WorkerMessage {
 	return acc.Worker.GetMessages()
 }
 
+func (acc *AccountTab) postAction(msg types.WorkerMessage) {
+	acc.logger.Printf("-> %T\n", msg)
+	acc.Worker.PostAction(msg)
+}
+
 func (acc *AccountTab) HandleMessage(msg types.WorkerMessage) {
-	switch msg.InResponseTo().(type) {
-	case types.Configure:
-		// Avoid printing passwords
-		acc.logger.Printf("<- %T\n", msg)
+	acc.logger.Printf("<- %T\n", msg)
+	switch msg.(type) {
+	case types.Ack:
+		// no-op
+	case types.ApproveCertificate:
+		// TODO: Ask the user
+		acc.logger.Println("Approving certificate")
+		acc.postAction(types.Ack{
+			Message: types.RespondTo(msg),
+		})
 	default:
-		acc.logger.Printf("<- %s", spew.Sdump(msg))
+		acc.postAction(types.Unsupported{
+			Message: types.RespondTo(msg),
+		})
 	}
 }
diff --git a/worker/imap/worker.go b/worker/imap/worker.go
index d3ef715..ceff34d 100644
--- a/worker/imap/worker.go
+++ b/worker/imap/worker.go
@@ -1,12 +1,13 @@
 package imap
 
 import (
+	"crypto/tls"
+	"crypto/x509"
 	"fmt"
 	"log"
 	"net/url"
 	"strings"
 
-	"github.com/davecgh/go-spew/spew"
 	"github.com/emersion/go-imap"
 	"github.com/emersion/go-imap-idle"
 	"github.com/emersion/go-imap/client"
@@ -54,6 +55,45 @@ func (w *IMAPWorker) PostAction(msg types.WorkerMessage) {
 	w.actions <- msg
 }
 
+func (w *IMAPWorker) postMessage(msg types.WorkerMessage) {
+	w.logger.Printf("=> %T\n", msg)
+	w.messages <- msg
+}
+
+func (w *IMAPWorker) verifyPeerCert(msg types.WorkerMessage) func(
+	rawCerts [][]byte, _ [][]*x509.Certificate) error {
+
+	return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
+		pool := x509.NewCertPool()
+		for _, rawCert := range rawCerts {
+			cert, err := x509.ParseCertificate(rawCert)
+			if err != nil {
+				return err
+			}
+			pool.AddCert(cert)
+		}
+
+		request := types.ApproveCertificate{
+			Message:  types.RespondTo(msg),
+			CertPool: pool,
+		}
+		w.postMessage(request)
+
+		response := <-w.actions
+		if response.InResponseTo() != request {
+			return fmt.Errorf("Expected UI to answer cert request")
+		}
+		switch response.(type) {
+		case types.Ack:
+			return nil
+		case types.Disconnect:
+			return fmt.Errorf("UI rejected certificate")
+		default:
+			return fmt.Errorf("Expected UI to answer cert request")
+		}
+	}
+}
+
 func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
 	switch msg := msg.(type) {
 	case types.Ping:
@@ -78,12 +118,14 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
 		w.config.scheme = u.Scheme
 		w.config.user = u.User
 	case types.Connect:
-		// TODO: populate TLS config
-
 		var (
 			c   *client.Client
 			err error
 		)
+		tlsConfig := &tls.Config{
+			InsecureSkipVerify:    true,
+			VerifyPeerCertificate: w.verifyPeerCert(&msg),
+		}
 		switch w.config.scheme {
 		case "imap":
 			c, err = client.Dial(w.config.addr)
@@ -92,12 +134,12 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
 			}
 
 			if !w.config.insecure {
-				if err := c.StartTLS(nil); err != nil {
+				if err := c.StartTLS(tlsConfig); err != nil {
 					return err
 				}
 			}
 		case "imaps":
-			c, err = client.DialTLS(w.config.addr, nil)
+			c, err = client.DialTLS(w.config.addr, tlsConfig)
 			if err != nil {
 				return err
 			}
@@ -131,40 +173,27 @@ func (w *IMAPWorker) handleMessage(msg types.WorkerMessage) error {
 	return nil
 }
 
-// Logs an action but censors passwords
-func (w *IMAPWorker) logAction(msg types.WorkerMessage) {
-	switch msg := msg.(type) {
-	case types.Configure:
-		src := msg.Config.Source
-		msg.Config.Source = "[obsfucated]"
-		w.logger.Printf("<= %s", spew.Sdump(msg))
-		msg.Config.Source = src
-	default:
-		w.logger.Printf("<= %s", spew.Sdump(msg))
-	}
-}
-
 func (w *IMAPWorker) Run() {
 	for {
 		select {
 		case msg := <-w.actions:
-			w.logAction(msg)
+			w.logger.Printf("<= %T\n", msg)
 			if err := w.handleMessage(msg); err == errUnsupported {
-				w.messages <- types.Unsupported{
+				w.postMessage(types.Unsupported{
 					Message: types.RespondTo(msg),
-				}
+				})
 			} else if err != nil {
-				w.messages <- types.Error{
+				w.postMessage(types.Error{
 					Message: types.RespondTo(msg),
 					Error:   err,
-				}
+				})
 			} else {
-				w.messages <- types.Ack{
+				w.postMessage(types.Ack{
 					Message: types.RespondTo(msg),
-				}
+				})
 			}
 		case update := <-w.updates:
-			w.logger.Printf("[= %s", spew.Sdump(update))
+			w.logger.Printf("[= %T", update)
 		}
 	}
 }
diff --git a/worker/types/messages.go b/worker/types/messages.go
index e83bd6b..5259342 100644
--- a/worker/types/messages.go
+++ b/worker/types/messages.go
@@ -1,6 +1,8 @@
 package types
 
 import (
+	"crypto/x509"
+
 	"git.sr.ht/~sircmpwn/aerc2/config"
 )
 
@@ -12,6 +14,16 @@ type Message struct {
 	inResponseTo WorkerMessage
 }
 
+func RespondTo(msg WorkerMessage) Message {
+	return Message{
+		inResponseTo: msg,
+	}
+}
+
+func (m Message) InResponseTo() WorkerMessage {
+	return m.inResponseTo
+}
+
 // Meta-messages
 
 type Ack struct {
@@ -27,7 +39,7 @@ type Unsupported struct {
 	Message
 }
 
-// Commands
+// Actions
 
 type Ping struct {
 	Message
@@ -46,12 +58,10 @@ type Disconnect struct {
 	Message
 }
 
-func RespondTo(msg WorkerMessage) Message {
-	return Message{
-		inResponseTo: msg,
-	}
-}
+// Messages
 
-func (m Message) InResponseTo() WorkerMessage {
-	return m.inResponseTo
+// Respond with an Ack to approve or Disconnect to reject
+type ApproveCertificate struct {
+	Message
+	CertPool *x509.CertPool
 }