diff -ruN fetchmail-6.3.8.orig/configure.ac fetchmail-6.3.8/configure.ac --- fetchmail-6.3.8.orig/configure.ac 2007-04-06 14:10:58.000000000 -0400 +++ fetchmail-6.3.8/configure.ac 2008-06-20 21:15:35.000000000 -0400 @@ -256,6 +256,22 @@ AC_MSG_RESULT(root-mode pid file will go in $dir) AC_DEFINE_UNQUOTED(PID_DIR, "$dir", directory for PID lock files) +AC_ARG_ENABLE(pwmd, + [ --enable-pwmd enable Password Manager Daemon support], + , [enable_pwmd=no]) + +if test "$enable_pwmd" = "yes"; then + PKG_CHECK_EXISTS([libpwmd], have_libpwmd=1, + AC_MSG_ERROR([Could not find libpwmd pkg-config module.])) + + + PKG_CHECK_MODULES([libpwmd], [libpwmd >= 5.0.0]) + AM_CONDITIONAL(HAVE_LIBPWMD, true) + AC_DEFINE(HAVE_LIBPWMD, 1, [Define if you have libPWMD installed.]) +else + AM_CONDITIONAL(HAVE_LIBPWMD, false) +fi + # We may have a fallback MDA available in case the socket open to the # local SMTP listener fails. Best to use procmail for this, as we know # it won't try delivering through local SMTP and cause a mail loop. diff -ruN fetchmail-6.3.8.orig/fetchmail.c fetchmail-6.3.8/fetchmail.c --- fetchmail-6.3.8.orig/fetchmail.c 2007-03-30 03:52:16.000000000 -0400 +++ fetchmail-6.3.8/fetchmail.c 2008-06-21 10:27:11.000000000 -0400 @@ -146,6 +146,297 @@ const char *iana_charset; +#ifdef HAVE_LIBPWMD +static void exit_with_pwmd_error(gpg_error_t error) +{ + gpg_err_code_t code = gpg_err_code(error); + + report(stderr, GT_("pwmd: error %i: %s\n"), code, pwmd_strerror(error)); + + if (pwm) { + pwmd_close(pwm); + pwm = NULL; + } + + /* Don't exit if daemonized. There may be other active accounts. */ + if (isatty(1)) + exit(PS_UNDEFINED); +} + +static void set_pwmd_pinentry_strings(const char *filename) +{ + gpg_error_t error; + char *result; + char buf[1000]; + + error = pwmd_command(pwm, &result, "OPTION CLIENT NAME=fetchmail"); + + if (error) { + exit_with_pwmd_error(error); + return; + } + + error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1); + + if (error) { + exit_with_pwmd_error(error); + return; + } + + if (run.pinentry_timeout > 0) { + error = pwmd_command(pwm, &result, "OPTION TIMEOUT %i", run.pinentry_timeout); + + if (error) { + exit_with_pwmd_error(error); + return; + } + } + + snprintf(buf, sizeof(buf), GT_("Password Manager Daemon: Fetchmail")); + error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, buf); + + if (error) { + exit_with_pwmd_error(error); + return; + } + + snprintf(buf, sizeof(buf), + GT_("A password is needed to open the file \"%s\". Please\n" + "enter the password below."), filename); + + error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_DESC, buf); + + if (error) { + exit_with_pwmd_error(error); + return; + } +} + +static int do_pwmd_connect(const char *socketname, const char *filename) +{ + static int init; + gpg_error_t error; + + if (!init) { + pwmd_init(); + init = 1; + } + + if ((pwm = pwmd_connect(socketname, &error)) == NULL) { + exit_with_pwmd_error(error); + return 1; + } + + set_pwmd_pinentry_strings(filename); + error = pwmd_open(pwm, filename); + + if (error) { + exit_with_pwmd_error(error); + return 1; + } + + return 0; +} +#endif + +#ifdef HAVE_LIBPWMD +static int get_pwmd_details(const char *pwmd_account, int protocol, + struct query *ctl) +{ + const char *prot = showproto(protocol); + gpg_error_t error; + char *result; + char *tmp = xstrdup(pwmd_account); + int i; + + for (i = 0; tmp[i]; i++) { + if (i && tmp[i] == '^') + tmp[i] = '\t'; + } + + /* + * Get the hostname for this protocol. Element path must be + * account->[protocol]->hostname. + */ + error = pwmd_command(pwm, &result, "GET %s\t%s\thostname", tmp, prot); + + if (error) { + if (gpg_err_code(error) == EPWMD_ELEMENT_NOT_FOUND) { + report(stderr, GT_("pwmd: %s->%s->hostname: %s\n"), pwmd_account, prot, pwmd_strerror(error)); + pwmd_close(pwm); + pwm = NULL; + + if (isatty(1)) + exit(PS_SYNTAX); + + return 1; + } + else { + exit_with_pwmd_error(error); + return 1; + } + } + + if (ctl->server.pollname != ctl->server.via) + xfree(ctl->server.via); + + ctl->server.via = xstrdup(result); + + if (ctl->server.queryname) + xfree(ctl->server.queryname); + + ctl->server.queryname = xstrdup(ctl->server.via); + + if (ctl->server.truename) + xfree(ctl->server.truename); + + ctl->server.truename = xstrdup(ctl->server.queryname); + pwmd_free_result(result); + + /* + * Server port. Fetchmail tries standard ports for known services so it + * should be alright if this element isn't found. ctl->server.protocol is + * already set. This sets ctl->server.service. + */ + error = pwmd_command(pwm, &result, "GET %s\t%s\tport", tmp, prot); + + if (error) { + if (gpg_err_code(error) == EPWMD_ELEMENT_NOT_FOUND) + report(stderr, GT_("pwmd: %s->%s->port: %s\n"), pwmd_account, prot, pwmd_strerror(error)); + else { + exit_with_pwmd_error(error); + return 1; + } + } + else { + if (ctl->server.service) + xfree(ctl->server.service); + + ctl->server.service = xstrdup(result); + pwmd_free_result(result); + } + + /* + * Get the remote username. Element must be account->username. + */ + error = pwmd_command(pwm, &result, "GET %s\tusername", tmp); + + if (error) { + if (gpg_err_code(error) == EPWMD_ELEMENT_NOT_FOUND) { + report(stderr, GT_("pwmd: %s->username: %s\n"), pwmd_account, pwmd_strerror(error)); + + if (!isatty(1)) { + pwmd_close(pwm); + pwm = NULL; + return 1; + } + } + else { + exit_with_pwmd_error(error); + return 1; + } + } + else { + if (ctl->remotename) + xfree(ctl->remotename); + + if (ctl->server.esmtp_name) + xfree(ctl->server.esmtp_name); + + ctl->remotename = xstrdup(result); + ctl->server.esmtp_name = xstrdup(result); + pwmd_free_result(result); + } + + /* + * Get the remote password. Element must be account->password. + */ + error = pwmd_command(pwm, &result, "GET %s\tpassword", tmp); + + if (error) { + if (gpg_err_code(error) == EPWMD_ELEMENT_NOT_FOUND) { + report(stderr, GT_("pwmd: %s->password: %s\n"), pwmd_account, pwmd_strerror(error)); + + if (!isatty(1)) { + pwmd_close(pwm); + pwm = NULL; + return 1; + } + } + else { + exit_with_pwmd_error(error); + return 1; + } + } + else { + if (ctl->password) + xfree(ctl->password); + + ctl->password= xstrdup(result); + pwmd_free_result(result); + } + +#ifdef SSL_ENABLE + /* + * If there is a ssl element and set to 1, enable ssl for this account. + * Element path must be account->[protocol]->ssl. + */ + error = pwmd_command(pwm, &result, "GET %s\t%s\tssl", tmp, prot); + + if (error) { + if (gpg_err_code(error) == EPWMD_ELEMENT_NOT_FOUND) { + report(stderr, GT_("pwmd: %s->%s->ssl: %s\n"), pwmd_account, prot, pwmd_strerror(error)); + + if (!isatty(1)) { + pwmd_close(pwm); + pwm = NULL; + return 1; + } + } + else { + exit_with_pwmd_error(error); + return 1; + } + } + else { + ctl->use_ssl = atoi(result) >= 1 ? FLAG_TRUE : FLAG_FALSE; + pwmd_free_result(result); + } + + /* + * account->[protocol]->sslfingerprint. + */ + error = pwmd_command(pwm, &result, "GET %s\t%s\tsslfingerprint", tmp, prot); + + if (error) { + if (gpg_err_code(error) == EPWMD_ELEMENT_NOT_FOUND) { + report(stderr, GT_("pwmd: %s->%s->sslfingerprint: %s\n"), pwmd_account, prot, pwmd_strerror(error)); + + if (!isatty(1)) { + pwmd_close(pwm); + pwm = NULL; + return 1; + } + } + else { + exit_with_pwmd_error(error); + return 1; + } + } + else { + if (ctl->sslfingerprint) + xfree(ctl->sslfingerprint); + + ctl->sslfingerprint = xstrdup(result); + pwmd_free_result(result); + } +#endif + + xfree(tmp); + return 0; +} +#endif + int main(int argc, char **argv) { int bkgd = FALSE; @@ -276,6 +567,9 @@ #ifdef KERBEROS_V5 "+KRB5" #endif /* KERBEROS_V5 */ +#ifdef HAVE_LIBPWMD + "+PWMD" +#endif /* HAVE_LIBPWMD */ ".\n"; printf(GT_("This is fetchmail release %s"), VERSION); fputs(features, stdout); @@ -621,6 +915,11 @@ * leaks... */ struct stat rcstat; +#ifdef HAVE_LIBPWMD + time_t now; + + time(&now); +#endif if (strcmp(rcfile, "-") == 0) { /* do nothing */ @@ -630,7 +929,15 @@ GT_("couldn't time-check %s (error %d)\n"), rcfile, errno); } +#ifdef HAVE_LIBPWMD + /* + * isatty() to make sure this is a background process since the + * lockfile is removed after each invokation. + */ + else if (!isatty(1) && rcstat.st_mtime > parsetime) +#else else if (rcstat.st_mtime > parsetime) +#endif { report(stdout, GT_("restarting fetchmail (%s changed)\n"), rcfile); @@ -737,6 +1044,22 @@ dofastuidl = 0; /* this is reset in the driver if required */ +#ifdef HAVE_LIBPWMD + /* + * At each poll interval, check the pwmd server for + * changes in host and auth settings. + */ + if (ctl->pwmd_file) { + if (do_pwmd_connect(ctl->pwmd_socket, ctl->pwmd_file)) + continue; + + if (get_pwmd_details(ctl->server.pollname, ctl->server.protocol, ctl)) + continue; + + pwmd_close(pwm); + pwm = NULL; + } +#endif querystatus = query_host(ctl); if (NUM_NONZERO(ctl->fastuidl)) @@ -1045,8 +1368,37 @@ if ((implicitmode = (optind >= argc))) { +#ifdef HAVE_LIBPWMD + for (ctl = querylist; ctl; ctl = ctl->next) { + ctl->active = !ctl->server.skip; + + if (ctl->pwmd_file) { + /* + * Cannot get an element path without a service. + */ + if (ctl->server.protocol <= 1) { + report(stderr, GT_("fetchmail: %s configuration invalid, pwmd_file requires a protocol specification\n"), + ctl->server.pollname); + pwmd_close(pwm); + exit(PS_SYNTAX); + } + + if (do_pwmd_connect(ctl->pwmd_socket, ctl->pwmd_file)) + continue; + + if (get_pwmd_details(ctl->server.pollname, ctl->server.protocol, + ctl)) + continue; + + pwmd_close(pwm); + pwm = NULL; + time(&rcstat.st_mtime); + } + } +#else for (ctl = querylist; ctl; ctl = ctl->next) ctl->active = !ctl->server.skip; +#endif } else for (; optind < argc; optind++) @@ -1067,6 +1419,29 @@ fprintf(stderr,GT_("Warning: multiple mentions of host %s in config file\n"),argv[optind]); ctl->active = TRUE; predeclared = TRUE; + +#ifdef HAVE_LIBPWMD + if (ctl->pwmd_file) { + /* + * Cannot get an element path without a service. + */ + if (ctl->server.protocol <= 1) { + report(stderr, GT_("%s configuration invalid, pwmd_file requires a protocol specification\n"), + ctl->server.pollname); + exit(PS_SYNTAX); + } + + if (do_pwmd_connect(ctl->pwmd_socket, ctl->pwmd_file)) + continue; + + if (get_pwmd_details(ctl->server.pollname, + ctl->server.protocol, ctl)) + continue; + + pwmd_close(pwm); + pwm = NULL; + } +#endif } if (!predeclared) @@ -1077,8 +1452,34 @@ * call later on. */ ctl = hostalloc((struct query *)NULL); - ctl->server.via = - ctl->server.pollname = xstrdup(argv[optind]); + +#ifdef HAVE_LIBPWMD + if (cmd_opts.pwmd_file) { + /* + * Cannot get an element path without a service. + */ + if (cmd_opts.server.protocol == 0 || cmd_opts.server.protocol == 1) { + report(stderr, GT_("Option --pwmd-file needs a service (-p) parameter.\n")); + exit(PS_SYNTAX); + } + + if (do_pwmd_connect(cmd_opts.pwmd_socket, cmd_opts.pwmd_file)) + continue; + + if (get_pwmd_details(argv[optind], cmd_opts.server.protocol, + ctl)) + continue; + + pwmd_close(pwm); + pwm = NULL; + } + else + ctl->server.via = + ctl->server.pollname = xstrdup(argv[optind]); +#else + ctl->server.via = + ctl->server.pollname = xstrdup(argv[optind]); +#endif ctl->active = TRUE; ctl->server.lead_server = (struct hostdata *)NULL; } diff -ruN fetchmail-6.3.8.orig/fetchmail.h fetchmail-6.3.8/fetchmail.h --- fetchmail-6.3.8.orig/fetchmail.h 2007-03-17 21:11:43.000000000 -0400 +++ fetchmail-6.3.8/fetchmail.h 2008-06-20 21:15:35.000000000 -0400 @@ -39,6 +39,10 @@ # include "trio/trio.h" #endif +#ifdef HAVE_LIBPWMD +#include +#endif + /* We need this for strstr */ #if !defined(HAVE_STRSTR) && !defined(strstr) char *strstr(const char *, const char *); @@ -177,6 +181,9 @@ char *pidfile; /** where to record the PID of daemon mode processes */ char *postmaster; char *properties; +#ifdef HAVE_LIBPWMD + int pinentry_timeout; +#endif int poll_interval; /** poll interval in seconds (daemon mode, 0 == off) */ flag bouncemail; flag spambounce; @@ -313,6 +320,11 @@ char *password; /* remote password to use */ struct idlist *mailboxes; /* list of mailboxes to check */ +#ifdef HAVE_LIBPWMD + char *pwmd_socket; /* socket to connect to */ + char *pwmd_file; /* file to open on the server */ +#endif + /* per-forwarding-target data */ struct idlist *smtphunt; /* list of SMTP hosts to try forwarding to */ struct idlist *domainlist; /* domainlist to fetch from */ @@ -455,6 +467,9 @@ extern char *sdps_envfrom; extern char *sdps_envto; #endif /* SDPS_ENABLE */ +#ifdef HAVE_LIBPWMD +pwm_t *pwm; /* the handle */ +#endif extern const char *iana_charset; /* IANA assigned charset name */ diff -ruN fetchmail-6.3.8.orig/Makefile.am fetchmail-6.3.8/Makefile.am --- fetchmail-6.3.8.orig/Makefile.am 2007-04-06 14:09:17.000000000 -0400 +++ fetchmail-6.3.8/Makefile.am 2008-06-20 21:15:35.000000000 -0400 @@ -18,6 +18,11 @@ pys= fetchmailconf.py pym= fetchmailconf.man +if HAVE_LIBPWMD +CFLAGS += @libpwmd_CFLAGS@ +LDFLAGS += @libpwmd_LIBS@ +endif + if HAVE_PYTHON nodist_bin_SCRIPTS= fetchmailconf python_PYTHON= $(pys) diff -ruN fetchmail-6.3.8.orig/options.c fetchmail-6.3.8/options.c --- fetchmail-6.3.8.orig/options.c 2006-08-14 19:04:02.000000000 -0400 +++ fetchmail-6.3.8/options.c 2008-06-20 21:15:35.000000000 -0400 @@ -53,12 +53,22 @@ LA_IDLE }; +#ifdef HAVE_LIBPWMD +static const char *shortoptions = + "O:C:G:?Vcsvd:NqL:f:i:p:UP:A:t:E:Q:u:akKFnl:r:S:Z:b:B:e:m:I:M:yw:D:"; +#else /* options still left: CgGhHjJoORTWxXYz */ static const char *shortoptions = "?Vcsvd:NqL:f:i:p:UP:A:t:E:Q:u:akKFnl:r:S:Z:b:B:e:m:I:M:yw:D:"; +#endif static const struct option longoptions[] = { /* this can be const because all flag fields are 0 and will never get set */ +#ifdef HAVE_LIBPWMD + {"pwmd-socket", required_argument, (int *) 0, 'C' }, + {"pwmd-file", required_argument, (int *) 0, 'G' }, + {"pinentry-timeout", required_argument, (int *) 0, 'O' }, +#endif {"help", no_argument, (int *) 0, '?' }, {"version", no_argument, (int *) 0, 'V' }, {"check", no_argument, (int *) 0, 'c' }, @@ -248,6 +258,17 @@ longoptions, &option_index)) != -1) { switch (c) { +#ifdef HAVE_LIBPWMD + case 'C': + ctl->pwmd_socket = prependdir(optarg, currentwd); + break; + case 'G': + ctl->pwmd_file = xstrdup(optarg); + break; + case 'O': + rctl->pinentry_timeout = atoi(optarg); + break; +#endif case 'V': versioninfo = TRUE; break; @@ -618,6 +639,12 @@ P(GT_(" --plugout specify external command to open smtp connection\n")); P(GT_(" -p, --protocol specify retrieval protocol (see man page)\n")); +#ifdef HAVE_LIBPWMD + P(GT_(" -C, --pwmd-socket pwmd socket path (~/.pwmd/socket)\n")); + P(GT_(" -G, --pwmd-file filename to use on the pwmd server\n")); + P(GT_(" -O, --pinentry-timeout seconds until pinentry is canceled\n")); +#endif + P(GT_(" -U, --uidl force the use of UIDLs (pop3 only)\n")); P(GT_(" --port TCP port to connect to (obsolete, use --service)\n")); P(GT_(" -P, --service TCP service to connect to (can be numeric TCP port)\n")); diff -ruN fetchmail-6.3.8.orig/rcfile_l.l fetchmail-6.3.8/rcfile_l.l --- fetchmail-6.3.8.orig/rcfile_l.l 2006-08-14 19:04:02.000000000 -0400 +++ fetchmail-6.3.8/rcfile_l.l 2008-06-20 21:15:35.000000000 -0400 @@ -114,6 +114,9 @@ user(name)? {SETSTATE(NAME); return USERNAME; } +pwmd_socket { return PWMD_SOCKET; } +pwmd_file { return PWMD_FILE; } +pinentry_timeout { return PINENTRY_TIMEOUT; } pass(word)? {SETSTATE(NAME); return PASSWORD; } folder(s)? { return FOLDER; } smtp(host)? { return SMTPHOST; } diff -ruN fetchmail-6.3.8.orig/rcfile_y.y fetchmail-6.3.8/rcfile_y.y --- fetchmail-6.3.8.orig/rcfile_y.y 2006-12-08 07:09:09.000000000 -0500 +++ fetchmail-6.3.8/rcfile_y.y 2008-06-20 21:15:35.000000000 -0400 @@ -63,7 +63,7 @@ %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL %token AUTHENTICATE TIMEOUT KPOP SDPS ENVELOPE QVIRTUAL -%token USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP +%token PINENTRY_TIMEOUT PWMD_SOCKET PWMD_FILE USERNAME PASSWORD FOLDER SMTPHOST FETCHDOMAINS MDA BSMTP LMTP %token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS %token INTERFACE MONITOR PLUGIN PLUGOUT %token IS HERE THERE TO MAP WILDCARD @@ -109,6 +109,13 @@ | SET NO INVISIBLE {run.invisible = FALSE;} | SET SHOWDOTS {run.showdots = FLAG_TRUE;} | SET NO SHOWDOTS {run.showdots = FLAG_FALSE;} + | SET PINENTRY_TIMEOUT optmap NUMBER { +#ifdef HAVE_LIBPWMD + run.pinentry_timeout = $4; +#else + yyerror(GT_("pwmd not enabled")); +#endif + } /* * The way the next two productions are written depends on the fact that @@ -243,6 +250,20 @@ userdef : USERNAME STRING {current.remotename = xstrdup($2);} | USERNAME mapping_list HERE | USERNAME STRING THERE {current.remotename = xstrdup($2);} + | PWMD_SOCKET STRING { +#ifdef HAVE_LIBPWMD + current.pwmd_socket = xstrdup($2); +#else + yyerror(GT_("pwmd not enabled")); +#endif + } + | PWMD_FILE STRING { +#ifdef HAVE_LIBPWMD + current.pwmd_file = xstrdup($2); +#else + yyerror(GT_("pwmd not enabled")); +#endif + } ; user0opts : /* EMPTY */ diff -ruN fetchmail-6.3.8.orig/README.PWMD fetchmail-6.3.8/README.PWMD --- fetchmail-6.3.8.orig/README.PWMD 1969-12-31 19:00:00.000000000 -0500 +++ fetchmail-6.3.8/README.PWMD 2008-06-21 10:29:04.000000000 -0400 @@ -0,0 +1,66 @@ +When compiled with pwmd (Password Manager Daemon) support (--enable-pwmd) +fetchmail can retrieve server details from pwmd. pwmd v1.11 and libpwmd v5.0.0 +or later are required. + +Three new configuration parameters are added: pwmd_socket (optional) to +specify the socket to connect to (default is ~/.pwmd/socket), pwmd_file +(required) which specifies the filename on the server to open, and a global +parameter pinentry_timeout (optional) which specifies the number of seconds +until pinentry is cancelled while waiting for the password. + +Three new command line options are also added: + --pwmd-socket, -C same as pwmd_socket + --pwmd-file, -G same as pwmd_file + --pinentry_timeout, -O same as pinentry_timeout + +If no pinentry timeout value is specified then the server default will be +used. + +The data that pwmd uses to serve clients is stored in an (encrypted) XML file. +You'll need to create the file you want fetchmail to use by connecting to the +server with a pwmd client (socat or pwmc from libpwmd) and send commands to +store the data. See COMMANDS included with pwmd for details. + +The password, if any, to open the encrypted data file is either cached on the +server (the file has been opened before), or gotten from pinentry(1). See the +pwmd(1) manual page for information about the ~/.pwmd/pinentry.conf file which +may contain DISPLAY and TTYNAME settings to let pinentry(1) know where to +prompt for the password. + +An account (e.g., pollname) may be an element path. Instead of separating the +elements with a TAB character, separate them with a '^'. + +Here are the elements that fetchmail uses: + + [...]elements in the element path (^ separated)[...] + + - Optional (--username/username) + - Optional (--password/password) + - Server protocol (must match the protocol + keyword from the rcfile or command line) + - Required (servername/via) + - Required (--service/protocol) + - Optional (--ssl/ssl) + - Optional (--sslfingerprint/sslfingerprint) + + + + +A minimal fetchmailrc might look like this: + + set pinentry_timeout 30 + poll isp proto POP3: + pwmd_file default + + poll myaccounts^isp proto IMAP: + pwmd_file default + + +Or from the command line: + + fetchmail -f fetchmailrc isp + fetchmail --pwmd-file somefile -p POP3 isp + + +Ben Kibbey +http://bjk.sourceforge.net/pwmd/.