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