i dove into the matter myself and came up with the attached patch.
please review, test, and fix as necessary (the patch is tested only on linux, where it obviously doesn't change much).

note: the patch is at the top of wip/master-next and won't apply on top of current master.
>From de0df019a316b8e99acf7f172836e37f97f3507f Mon Sep 17 00:00:00 2001
From: Oswald Buddenhagen <o...@users.sf.net>
Date: Wed, 27 Nov 2019 17:13:44 +0100
Subject: [PATCH] add option to get password from macOS Keychain

this is better than using PassCmd, as it allows the keychain manager to
identify the calling process and therefore use a selective whitelist.

unlike in the now removed example, we use an "internet password" for the
imap protocol, rather than a "generic password" - this seems more
appropriate.

based on a patch by
CCMAIL: Oliver Runge <oliver.ru...@gmail.com>
---
 configure.ac        | 34 +++++++++++++++++++++++++++++++++-
 src/Makefile.am     |  2 +-
 src/drv_imap.c      | 43 +++++++++++++++++++++++++++++++++++++++++++
 src/mbsync.1        | 23 +++++++++++++++++++++++
 src/mbsyncrc.sample |  5 -----
 5 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/configure.ac b/configure.ac
index 811db2c..50e5ddb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,7 +1,9 @@
 AC_INIT([isync], [1.4.0])
 AC_CONFIG_HEADERS([autodefs.h])
-AM_INIT_AUTOMAKE
 
+AC_CANONICAL_TARGET
+
+AM_INIT_AUTOMAKE
 AM_MAINTAINER_MODE
 
 AC_PROG_CC_C99
@@ -198,6 +200,29 @@ fi
 
 AM_CONDITIONAL(with_mdconvert, test "x$ac_cv_berkdb4" = xyes)
 
+case $target_os in
+darwin*)
+    darwin=yes
+;;
+*)
+    darwin=no
+;;
+esac
+
+AC_ARG_WITH(
+    macos-keychain,
+    [AS_HELP_STRING([--with-macos-keychain], [Support macOS keychain])],
+    [have_macos_keychain=$withval],
+    [have_macos_keychain=$darwin])
+if test "x$have_macos_keychain" != xno; then
+    if test $darwin = no; then
+        AC_MSG_ERROR([Cannot use macOS Keychain outside macOS.])
+    fi
+    have_macos_keychain=yes
+    AC_DEFINE(HAVE_MACOS_KEYCHAIN, 1, [Define to 1 if you have the macOS Keychain Services API.])
+    AC_SUBST(KEYCHAIN_LIBS, ["-Wl,-framework,Security"])
+fi
+
 AC_CONFIG_FILES([Makefile src/Makefile isync.spec])
 AC_OUTPUT
 
@@ -222,4 +247,11 @@ if test "x$ac_cv_berkdb4" = xyes; then
 else
     AC_MSG_RESULT([Not using Berkeley DB])
 fi
+if test $darwin = yes; then
+    if test "x$have_macos_keychain" = xyes; then
+        AC_MSG_RESULT([Using macOS Keychain])
+    else
+        AC_MSG_RESULT([Not using macOS Keychain])
+    fi
+fi
 AC_MSG_RESULT()
diff --git a/src/Makefile.am b/src/Makefile.am
index 63c4e22..76dff34 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,5 +1,5 @@
 mbsync_SOURCES = main.c sync.c config.c util.c socket.c driver.c drv_imap.c drv_maildir.c drv_proxy.c
-mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS)
+mbsync_LDADD = $(DB_LIBS) $(SSL_LIBS) $(SOCK_LIBS) $(SASL_LIBS) $(Z_LIBS) $(KEYCHAIN_LIBS)
 noinst_HEADERS = common.h config.h driver.h sync.h socket.h
 
 drv_proxy.$(OBJEXT): drv_proxy.inc
diff --git a/src/drv_imap.c b/src/drv_imap.c
index 14295be..2b8f9b8 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -41,6 +41,10 @@
 # include <sasl/saslutil.h>
 #endif
 
+#ifdef HAVE_MACOS_KEYCHAIN
+# include <Security/Security.h>
+#endif
+
 #ifdef HAVE_LIBSSL
 enum { SSL_None, SSL_STARTTLS, SSL_IMAPS };
 #endif
@@ -58,6 +62,9 @@ typedef struct imap_server_conf {
 	string_list_t *auth_mechs;
 #ifdef HAVE_LIBSSL
 	char ssl_type;
+#endif
+#ifdef HAVE_MACOS_KEYCHAIN
+	char use_keychain;
 #endif
 	char failed;
 } imap_server_conf_t;
@@ -1996,6 +2003,31 @@ ensure_password( imap_server_conf_t *srvc )
 	if (!srvc->pass) {
 		if (srvc->pass_cmd) {
 			srvc->pass = cred_from_cmd( "PassCmd", srvc->pass_cmd, srvc->name );
+#ifdef HAVE_MACOS_KEYCHAIN
+		} else if (srvc->use_keychain) {
+			void *password_data;
+			UInt32 password_length;
+			OSStatus ret = SecKeychainFindInternetPassword(
+					NULL,  // keychainOrArray
+					strlen( srvc->sconf.host ), srvc->sconf.host,
+					0, NULL,  // securityDomain
+					strlen( srvc->user ), srvc->user,
+					0, NULL,  // path
+					0,  // port - we could use it, but it seems pointless
+					kSecProtocolTypeIMAP,
+					kSecAuthenticationTypeDefault,
+					&password_length, &password_data,
+					NULL );  // itemRef
+			if (ret != errSecSuccess) {
+				CFStringRef errmsg = SecCopyErrorMessageString( ret, NULL );
+				error( "Looking up Keychain failed: %s\n",
+				       CFStringGetCStringPtr( errmsg, kCFStringEncodingUTF8 ) );
+				CFRelease( errmsg );
+				return NULL;
+			}
+			srvc->pass = nfstrndup( password_data, password_length );
+			SecKeychainItemFreeContent( NULL, password_data );
+#endif /* HAVE_MACOS_KEYCHAIN */
 		} else {
 			flushn();
 			char prompt[80];
@@ -3300,6 +3332,10 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
 			server->pass = nfstrdup( cfg->val );
 		else if (!strcasecmp( "PassCmd", cfg->cmd ))
 			server->pass_cmd = nfstrdup( cfg->val );
+#ifdef HAVE_MACOS_KEYCHAIN
+		else if (!strcasecmp( "UseKeychain", cfg->cmd ))
+			server->use_keychain = parse_bool( cfg );
+#endif
 		else if (!strcasecmp( "Port", cfg->cmd )) {
 			int port = parse_int( cfg );
 			if ((unsigned)port > 0xffff) {
@@ -3469,6 +3505,13 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
 			cfg->err = 1;
 			return 1;
 		}
+#ifdef HAVE_MACOS_KEYCHAIN
+		if (server->use_keychain && (server->pass || server->pass_cmd)) {
+			error( "%s '%s' has UseKeychain enabled despite specifying Pass/PassCmd\n", type, name );
+			cfg->err = 1;
+			return 1;
+		}
+#endif
 #ifdef HAVE_LIBSSL
 		if ((use_tlsv1 & use_tlsv11 & use_tlsv12 & use_tlsv13) != -1 || use_imaps >= 0 || require_ssl >= 0) {
 			if (server->ssl_type >= 0 || server->sconf.ssl_versions >= 0) {
diff --git a/src/mbsync.1 b/src/mbsync.1
index 4f6dfae..7fb6447 100644
--- a/src/mbsync.1
+++ b/src/mbsync.1
@@ -341,6 +341,29 @@ Prepend \fB+\fR to the command to indicate that it produces TTY output
 messier output.
 .
 .TP
+\fBUseKeychain\fR \fByes\fR|\fBno\fR
+Whether to use the macOS Keychain to obtain the password.
+(Default: \fBno\fR)
+.IP
+The neccessary keychain item can be created this way:
+.RS
+.IP
+.nh
+.B security
+.B add-internet-password \-r imap \-s
+.I Host
+.B \-a
+.I User
+.B \-w
+.I password
+[
+.B \-T
+.I /path/to/mbsync
+]
+.hy
+.RE
+.
+.TP
 \fBTunnel\fR \fIcommand\fR
 Specify a command to run to establish a connection rather than opening a TCP
 socket.  This allows you to run an IMAP session over an SSH tunnel, for
diff --git a/src/mbsyncrc.sample b/src/mbsyncrc.sample
index abf74e5..a9d9b13 100644
--- a/src/mbsyncrc.sample
+++ b/src/mbsyncrc.sample
@@ -21,11 +21,6 @@ Pass xxxxxxxx
 #PassCmd "gpg --quiet --for-your-eyes-only --decrypt $HOME/imappassword.gpg"
 # Fetch password from pwmd (http://pwmd.sourceforge.net/):
 #PassCmd "echo -ne 'GET myIsp\\tpassword' | pwmc datafile"
-# On macOS, run "KeyChain Access" -- File->New Password Item. Fill out form using
-#  "Keychain Item Name" http://IMAPSERVER  (note: the "http://"; is a hack)
-#  "Account Name" USERNAME
-#  "Password" PASSWORD
-#PassCmd "/usr/bin/security find-internet-password -w -a USERNAME -s IMAPSERVER ~/Library/Keychains/login.keychain"
 
 Channel work
 Master :work:
-- 
2.23.0.1.g9111c1b4df

_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to