From 485a2b6cc52be6ea6b38baa968b5cbdb0bcab296 Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Tue, 20 Nov 2018 21:22:40 +0000
Subject: [PATCH] Return mail_crypt_save_version=2 on responses

---
 README.md           |  7 +++++++
 dovecot/keyproxy.go | 34 ++++++++++++++++++++++++++++------
 2 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md
index 63ef43fb..4416aa71 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,13 @@ private keys from LDAP, and serve the *unencrypted* keys to Dovecot
 using its [dict proxy
 protocol](https://wiki2.dovecot.org/AuthDatabase/Dict).
 
+*NOTE* that passdb lookups using *dovecot-keylookupd* contain the
+cleartext password as part of the key, which may be logged in case of
+error! This is currently a huge limitation of this solution, but there
+seems to be no workaround that does not involve switching to a
+fork()-based solution (like the checkpassword script). That might be a
+better solution long-term.
+
 TODO: explain the lookup protocol.
 
 # Configuration
diff --git a/dovecot/keyproxy.go b/dovecot/keyproxy.go
index b831c4c1..8ef42139 100644
--- a/dovecot/keyproxy.go
+++ b/dovecot/keyproxy.go
@@ -42,19 +42,41 @@ func (c *Config) check() error {
 	return c.LDAPConfig.Valid()
 }
 
+// The response returned to userdb lookups. It contains the user's
+// public key as a global key for the mail_crypt plugin, and it sets
+// mail_crypt_save_version to 2. The idea is that you would then set
+// mail_crypt_save_version = 0 in the global Dovecot configuration,
+// which would then disable encryption for users without encryption
+// keys. For details on what this means, see
+// https://wiki2.dovecot.org/Plugins/MailCrypt.
 type userdbResponse struct {
-	PublicKey string `json:"mail_crypt_global_public_key"`
+	PublicKey   string `json:"mail_crypt_global_public_key"`
+	SaveVersion int    `json:"mail_crypt_save_version"`
 }
 
+func newUserDBResponse(publicKey string) *userdbResponse {
+	return &userdbResponse{
+		PublicKey:   publicKey,
+		SaveVersion: 2,
+	}
+}
+
+// The response returned to passdb lookups. We return the user's
+// private key and the mail_crypt_save_version attribute as userdb
+// parameters (hence the 'userdb_' prefix), and set the noauthenticate
+// bit to inform Dovecot that this lookup is only meant to provide
+// additional data, not authentication.
 type passdbResponse struct {
-	PrivateKey string `json:"userdb_mail_crypt_global_private_key"`
-	NoAuth     bool   `json:"noauthenticate"`
+	PrivateKey  string `json:"userdb_mail_crypt_global_private_key"`
+	SaveVersion int    `json:"userdb_mail_crypt_save_version"`
+	NoAuth      bool   `json:"noauthenticate"`
 }
 
 func newPassDBResponse(privateKey string) *passdbResponse {
 	return &passdbResponse{
-		PrivateKey: privateKey,
-		NoAuth:     true,
+		PrivateKey:  privateKey,
+		SaveVersion: 2,
+		NoAuth:      true,
 	}
 }
 
@@ -123,7 +145,7 @@ func (s *KeyLookupProxy) lookupUserdb(ctx context.Context, username string) (int
 		return nil, false, nil
 	}
 	log.Printf("userdb lookup for %s", username)
-	return &userdbResponse{PublicKey: s.b64encode(pub)}, true, nil
+	return newUserDBResponse(s.b64encode(pub)), true, nil
 }
 
 func (s *KeyLookupProxy) lookupPassdb(ctx context.Context, username, password string) (interface{}, bool, error) {
-- 
GitLab