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 2007-07-21 15:09:48.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 >= 4.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 2007-07-21 15:18:21.000000000 -0400 @@ -146,6 +146,92 @@ 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); + + exit(PS_UNDEFINED); +} + +static void set_pwmd_pinentry_strings(const char *filename) +{ + char buf[255]; + gpg_error_t error; + + /* + * Use pinentry for password retrieval. Since version 0.3 of pwmd + * there is an option to push files into the file cache when the + * server starts up. For a daemonized fetchmail, this may be + * required because there won't be a way to get the key without a + * tty that pinentry requires. + */ + error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY, 1); + + if (error) + exit_with_pwmd_error(error); + + if (pwmd_pinentry_path) { + error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_PATH, pwmd_pinentry_path); + + if (error) + exit_with_pwmd_error(error); + } + + /* + * Set the text to be used in the pinentry dialog. + */ + error = pwmd_setopt(pwm, PWMD_OPTION_PINENTRY_TITLE, + GT_("Password Manager Daemon: Fetchmail")); + + if (error) + exit_with_pwmd_error(error); + + 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); +} + +static void pwmd_do_connect(const char *socketname, const char *filename) +{ + static int init; + gpg_error_t error; + + if (!init) { + pwmd_init(); + init = 1; + } + + /* + * Try and connect to pwmd. + */ + if ((pwm = pwmd_connect(socketname, &error)) == NULL) + exit_with_pwmd_error(error); + + set_pwmd_pinentry_strings(filename); + + /* + * Try and open the file so we can later get account/server credentials. If + * the file is not cached on the server, pinentry(1) will ask for the + * password. + */ + error = pwmd_open(pwm, filename); + + if (error) + exit_with_pwmd_error(error); +} +#endif + int main(int argc, char **argv) { int bkgd = FALSE; @@ -276,6 +362,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); @@ -988,6 +1077,166 @@ #undef FLAG_MERGE } +#ifdef HAVE_LIBPWMD +static char *protocol_to_service(int protocol) +{ + switch (protocol) { +#ifdef POP2_ENABLE + case P_POP2: + return "POP2"; +#endif +#ifdef POP3_ENABLE + case P_POP3: + return "POP3"; +#endif +#ifdef IMAP_ENABLE + case P_IMAP: + return "IMAP"; +#endif + case P_APOP: + return "APOP"; + case P_RPOP: + return "RPOP"; + case P_ETRN: + return "ETRN"; + case P_ODMR: + return "ODMR"; + default: + return NULL; + } + + return NULL; +} + +static int get_pwmd_details(const char *pwmd_account, int protocol, + struct query *ctl) +{ + char *prot = protocol_to_service(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); + exit(PS_SYNTAX); + } + else + exit_with_pwmd_error(error); + } + + ctl->server.pollname = xstrdup(pwmd_account); + ctl->server.via = xstrdup(result); + pwmd_free_result(result); + + /* + * Server port. Element path must be account->[protocol]->port. Should be + * required because the element path wouldn't exist with out it. But + * maybe not because fetchmail trys standard ports if not specified. This + * may be a security risk. Might be better to have 'ssl' required. + */ + 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)); + pwmd_close(pwm); + exit(PS_SYNTAX); + } + else + exit_with_pwmd_error(error); + } + + 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)); + else + exit_with_pwmd_error(error); + } + else { + 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)); + else + exit_with_pwmd_error(error); + } + else { + 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)); + else + exit_with_pwmd_error(error); + } + else { + ctl->use_ssl = atoi(result); + ctl->use_ssl = (ctl->use_ssl >= 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)); + else + exit_with_pwmd_error(error); + } + else { + ctl->sslfingerprint = xstrdup(result); + pwmd_free_result(result); + } +#endif + + xfree(tmp); + return 0; +} +#endif + /** Load configuration files. * \return - true if no servers found on the command line * - false if servers found on the command line */ @@ -1045,8 +1294,31 @@ 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); + } + + pwmd_do_connect(ctl->pwmd_socket, ctl->pwmd_file); + get_pwmd_details(ctl->server.pollname, + ctl->server.protocol, ctl); + pwmd_close(pwm); + } + } +#else for (ctl = querylist; ctl; ctl = ctl->next) ctl->active = !ctl->server.skip; +#endif } else for (; optind < argc; optind++) @@ -1067,6 +1339,24 @@ 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); + } + + pwmd_do_connect(ctl->pwmd_socket, ctl->pwmd_file); + get_pwmd_details(ctl->server.pollname, + ctl->server.protocol, ctl); + pwmd_close(pwm); + } +#endif } if (!predeclared) @@ -1077,8 +1367,29 @@ * 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); + } + + pwmd_do_connect(cmd_opts.pwmd_socket, cmd_opts.pwmd_file); + get_pwmd_details(argv[optind], cmd_opts.server.protocol, + ctl); + pwmd_close(pwm); + } + 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 2007-07-21 15:09:50.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 *); @@ -313,6 +317,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 +464,10 @@ extern char *sdps_envfrom; extern char *sdps_envto; #endif /* SDPS_ENABLE */ +#ifdef HAVE_LIBPWMD +pwm_t *pwm; /* the handle */ +char *pwmd_pinentry_path; /* location of the pinentry binary */ +#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 2007-07-21 15:09:50.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 2007-07-21 15:09:50.000000000 -0400 @@ -53,12 +53,22 @@ LA_IDLE }; +#ifdef HAVE_LIBPWMD +static const char *shortoptions = + "C:G:Y:?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-path", required_argument, (int *) 0, 'Y' }, +#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 'Y': + pwmd_pinentry_path = xstrdup(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_(" -Y, --pinentry-path path of the pinentry binary\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 2007-07-21 15:09:50.000000000 -0400 @@ -114,6 +114,8 @@ user(name)? {SETSTATE(NAME); return USERNAME; } +pwmd_socket { return PWMD_SOCKET; } +pwmd_file { return PWMD_FILE; } 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 2007-07-21 15:09:50.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 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 @@ -243,6 +243,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 2007-07-21 17:17:55.000000000 -0400 @@ -0,0 +1,45 @@ +When compiled with pwmd (Password Manager Daemon) support (--enable-pwmd), +fetchmail can retrieve server details from pwmd. Two new configuration +parameters are added: pwmd_socket (optional) to specify the socket to connect +to (default is ~/.pwmd/socket) and pwmd_file (required) which specifies the +filename on the server to open. + +Three new command line options are added: + --pwmd-socket, -C same as pwmd_socket + --pwmd-file, -G same as pwmd_file + --pinentry-path, -Y the full path of the pinentry program + (/usr/bin/pinentry) + +The data that fetchmail will use is stored in an encrypted XML file and has +the following structure: + + + - Optional (--username/username) + - Optional (--password/password) + - POP3/IMAP/etc. + - Required (servername/via) + - Required (--service/protocol) + - Optional (--ssl/ssl) + - Optional (--sslfingerprint/sslfingerprint) + + + +An account (pollname) may be an element path. Instead of separating the +elements with a TAB character, separate them with a '!'. A minimal fetchmailrc +might look like this: + +poll isp proto POP3: + pwmd_file default + +poll myaccounts!isp proto POP3: + pwmd_file default + +Or from the command line: + fetchmail -f fetchmailrc isp + fetchmail --pwmd-file somefile -p POP3 isp + +The password to open the encrypted data file is either cached on the server +(the file has been opened before), or gotten from pinentry(1). + +Ben Kibbey +http://bjk.sourceforge.net/pwmd/.