forked from pool/fetchmail
d3007028df
- Update to 6.4.32: * Use configure to find rst2html, some systems install it only with .py suffix, others only without, and some install both. * Update README.maintainer * Translations updated. - Reapplied patches - Add 44-uncorrupt_runfetchmail.patch to clean up some contrib/ scripts (gl#fetchmail/fetchmail#44). OBS-URL: https://build.opensuse.org/request/show/991826 OBS-URL: https://build.opensuse.org/package/show/server:mail/fetchmail?expand=0&rev=137
303 lines
10 KiB
Diff
303 lines
10 KiB
Diff
From: Matthew Ogilvie <mmogilvi+fml@zoho.com>
|
|
Date: Sun, 28 May 2017 00:01:02 -0600
|
|
Subject: add passwordfile and passwordfd options
|
|
Git-repo: https://gitlab.com/fetchmail/fetchmail.git
|
|
Git-commit: cdd7182f65734c97723ba5f282040e08d830e650
|
|
|
|
---
|
|
fetchmail.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
|
|
fetchmail.h | 2 +
|
|
fetchmail.man | 40 +++++++++++++++++++++++++++-
|
|
options.c | 16 +++++++++++
|
|
rcfile_l.l | 2 +
|
|
rcfile_y.y | 6 ++++
|
|
6 files changed, 145 insertions(+), 3 deletions(-)
|
|
|
|
--- a/fetchmail.c
|
|
+++ b/fetchmail.c
|
|
@@ -451,7 +451,7 @@ int main(int argc, char **argv)
|
|
/* Server won't care what the password is, but there
|
|
must be some non-null string here. */
|
|
ctl->password = ctl->remotename;
|
|
- else
|
|
+ else if (!ctl->passwordfile && ctl->passwordfd==-1)
|
|
{
|
|
netrc_entry *p;
|
|
|
|
@@ -618,8 +618,81 @@ int main(int argc, char **argv)
|
|
if (ctl->active && !(implicitmode && ctl->server.skip)
|
|
&& !NO_PASSWORD(ctl) && !ctl->password)
|
|
{
|
|
- if (!isatty(0))
|
|
+ if (ctl->passwordfd != -1)
|
|
{
|
|
+ char msg[PASSWORDLEN+1];
|
|
+ char *mi;
|
|
+
|
|
+ /* Read one character at a time to avoid reading too
|
|
+ * much if more than one password sent in through this FD
|
|
+ * (although that would be a questionable practice).
|
|
+ */
|
|
+ for (mi = msg; mi<msg+sizeof(msg)-1; ++mi) {
|
|
+ int res = read(ctl->passwordfd, mi, 1);
|
|
+ if(res == -1) {
|
|
+ int saveErrno = errno;
|
|
+ fprintf(stderr,
|
|
+ GT_("fetchmail: unable to read password from fd=%d: %s\n"),
|
|
+ ctl->passwordfd,
|
|
+ strerror(saveErrno));
|
|
+ memset(msg, 0x55, mi-msg);
|
|
+ return PS_AUTHFAIL;
|
|
+ }
|
|
+ if (res == 0 || *mi == '\n')
|
|
+ break;
|
|
+ }
|
|
+ *mi = '\0';
|
|
+ if (mi == msg) {
|
|
+ fprintf(stderr,
|
|
+ GT_("fetchmail: empty password read from fd=%d\n"),
|
|
+ ctl->passwordfd);
|
|
+ return PS_AUTHFAIL;
|
|
+ }
|
|
+
|
|
+ ctl->password = xstrdup(msg);
|
|
+ memset(msg, 0x55, mi-msg);
|
|
+ } else if (ctl->passwordfile) {
|
|
+ int fd = open(ctl->passwordfile, O_RDONLY);
|
|
+ char msg[PASSWORDLEN+1];
|
|
+ char *newline;
|
|
+ int res;
|
|
+
|
|
+ if (fd == -1) {
|
|
+ int saveErrno = errno;
|
|
+ fprintf(stderr,
|
|
+ GT_("fetchmail: unable to open %s: %s\n"),
|
|
+ ctl->passwordfile,
|
|
+ strerror(saveErrno));
|
|
+ return PS_AUTHFAIL;
|
|
+ }
|
|
+
|
|
+ res = read(fd, msg, sizeof(msg)-1);
|
|
+ if (res == -1 || close(fd) == -1) {
|
|
+ int saveErrno = errno;
|
|
+ fprintf(stderr,
|
|
+ GT_("fetchmail: error reading %s: %s\n"),
|
|
+ ctl->passwordfile,
|
|
+ strerror(saveErrno));
|
|
+ return PS_AUTHFAIL;
|
|
+ }
|
|
+ msg[res] = '\0';
|
|
+
|
|
+ newline = memchr(msg, '\n', res);
|
|
+ if (newline != NULL) {
|
|
+ *newline = '\0';
|
|
+ }
|
|
+
|
|
+ if (strlen(msg) == 0) {
|
|
+ fprintf(stderr,
|
|
+ GT_("fetchmail: empty password read from %s\n"),
|
|
+ ctl->passwordfile);
|
|
+ memset(msg, 0x55, res);
|
|
+ return PS_AUTHFAIL;
|
|
+ }
|
|
+
|
|
+ ctl->password = xstrdup(msg);
|
|
+ memset(msg, 0x55, res);
|
|
+ } else if (!isatty(0)) {
|
|
fprintf(stderr,
|
|
GT_("fetchmail: can't find a password for %s@%s.\n"),
|
|
ctl->remotename, ctl->server.pollname);
|
|
@@ -1035,6 +1108,10 @@ static void optmerge(struct query *h2, s
|
|
FLAG_MERGE(wildcard);
|
|
STRING_MERGE(remotename);
|
|
STRING_MERGE(password);
|
|
+ FLAG_MERGE(passwordfile);
|
|
+ if (force ? h1->passwordfd!=-1 : h2->passwordfd==-1) {
|
|
+ h2->passwordfd = h1->passwordfd;
|
|
+ }
|
|
STRING_MERGE(mda);
|
|
STRING_MERGE(bsmtp);
|
|
FLAG_MERGE(listener);
|
|
@@ -1099,6 +1176,7 @@ static int load_params(int argc, char **
|
|
def_opts.smtp_socket = -1;
|
|
def_opts.smtpaddress = (char *)0;
|
|
def_opts.smtpname = (char *)0;
|
|
+ def_opts.passwordfd = -1;
|
|
def_opts.server.protocol = P_AUTO;
|
|
def_opts.server.timeout = CLIENT_TIMEOUT;
|
|
def_opts.server.esmtp_name = user;
|
|
--- a/fetchmail.h
|
|
+++ b/fetchmail.h
|
|
@@ -326,6 +326,8 @@ struct query
|
|
int wildcard; /* should unmatched names be passed through */
|
|
char *remotename; /* remote login name to use */
|
|
char *password; /* remote password to use */
|
|
+ char *passwordfile; /* filename; first line contains password */
|
|
+ int passwordfd; /* fileno that password will be piped into */
|
|
struct idlist *mailboxes; /* list of mailboxes to check */
|
|
|
|
/* per-forwarding-target data */
|
|
--- a/fetchmail.man
|
|
+++ b/fetchmail.man
|
|
@@ -949,6 +949,37 @@ The default is your login name on the cl
|
|
\fBfetchmail\fP.
|
|
See USER AUTHENTICATION below for a complete description.
|
|
.TP
|
|
+.B \-\-passwordfile <filename>
|
|
+(Keyword: passwordfile)
|
|
+.br
|
|
+Specifies a file name from which to read the first line to use as the password.
|
|
+Useful if something changes the password/token often without regenerating a
|
|
+long fetchmailrc file, such as with typical xoauth2 authentication tokens.
|
|
+Protect the file with appropriate permissions to avoid leaking your password.
|
|
+Fetchmail might not re-read the file in daemon mode (-d) unless the
|
|
+fetchmailrc file also changes, so it might make sense to run it in
|
|
+non-daemon mode from some other background process (cron and/or whatever
|
|
+updates the password).
|
|
+.TP
|
|
+.B \-\-passwordfd <integer>
|
|
+(Keyword: passwordfd)
|
|
+.br
|
|
+Specifies a file descriptor number inherited from the calling process,
|
|
+from which fetchmail should read one line to use as the password.
|
|
+The descriptor will usually be the receiving end of a pipe (equivalent
|
|
+to "something | fetchmail \-\-passwordfd 5 5<\&0"),
|
|
+although it could also be a redirected input file
|
|
+(equivalent to "fetchmail \-\-passwordfd 5 5</path/to/file").
|
|
+Useful if something wants to manage password ownership more securely
|
|
+than files, or if the password/token changes often,
|
|
+such as with typical xoauth2 authentication tokens. Normal interactive
|
|
+mode passwords requires that standard input is a terminal and disables
|
|
+echo, but passwordfd does not care. Do not do something
|
|
+like "echo 'password' | fetchmail ...", since echo's arguments are
|
|
+likely to (briefly) be publicly visible in process listings.
|
|
+This probably doesn't interact well with deamon mode: when will it
|
|
+re-read a new password?
|
|
+.TP
|
|
.B \-I <specification> | \-\-interface <specification>
|
|
(Keyword: interface)
|
|
.br
|
|
@@ -1040,7 +1071,8 @@ setting also allows the non-standard "xo
|
|
the same token) if the server only claims to support "xoauth2".
|
|
External tools are necessary to obtain
|
|
such tokens. Access tokens often expire fairly quickly (e.g. 1 hour),
|
|
-and new ones need to be generated from renewal tokens. See the
|
|
+and new ones need to be generated from renewal tokens, so the
|
|
+"passwordfile", "passwordfd", or "pwmd_*" options may be useful. See the
|
|
oauth2.py script from
|
|
.URL https://github.com/google/gmail-oauth2-tools/wiki/OAuth2DotPyRunThrough "Google's Oauth2 Run Through" ,
|
|
and other oauth2 documentation. For services like gmail, an "App Password"
|
|
@@ -1953,6 +1985,12 @@ T}
|
|
pass[word] \& \& T{
|
|
Specify remote account password
|
|
T}
|
|
+passwordfile \-\-... \& T{
|
|
+File name with password in first line.
|
|
+T}
|
|
+passwordfd \-\-... \& T{
|
|
+Inherited file descriptor from which to read one line for the password.
|
|
+T}
|
|
ssl \& \& T{
|
|
Connect to server over the specified base protocol using SSL encryption
|
|
T}
|
|
--- a/options.c
|
|
+++ b/options.c
|
|
@@ -31,6 +31,8 @@ enum {
|
|
LA_POSTMASTER,
|
|
LA_NOBOUNCE,
|
|
LA_AUTH,
|
|
+ LA_PASSWORDFILE,
|
|
+ LA_PASSWORDFD,
|
|
LA_FETCHDOMAINS,
|
|
LA_BSMTP,
|
|
LA_LMTP,
|
|
@@ -94,6 +96,8 @@ static const struct option longoptions[]
|
|
{"port", required_argument, (int *) 0, 'P' },
|
|
{"service", required_argument, (int *) 0, 'P' },
|
|
{"auth", required_argument, (int *) 0, LA_AUTH},
|
|
+ {"passwordfile", required_argument, (int *) 0, LA_PASSWORDFILE },
|
|
+ {"passwordfd", required_argument, (int *) 0, LA_PASSWORDFD },
|
|
{"timeout", required_argument, (int *) 0, 't' },
|
|
{"envelope", required_argument, (int *) 0, 'E' },
|
|
{"qvirtual", required_argument, (int *) 0, 'Q' },
|
|
@@ -261,6 +265,7 @@ int parsecmdline (int argc /** argument
|
|
|
|
memset(ctl, '\0', sizeof(struct query)); /* start clean */
|
|
ctl->smtp_socket = -1;
|
|
+ ctl->passwordfd = -1;
|
|
|
|
while (!errflag &&
|
|
(c = getopt_long(argc,argv,shortoptions,
|
|
@@ -428,6 +433,17 @@ int parsecmdline (int argc /** argument
|
|
errflag++;
|
|
}
|
|
break;
|
|
+ case LA_PASSWORDFILE:
|
|
+ ctl->passwordfile = optarg;
|
|
+ break;
|
|
+ case LA_PASSWORDFD:
|
|
+ ctl->passwordfd = xatoi(optarg, &errflag);
|
|
+ if (ctl->passwordfd < 0) {
|
|
+ fprintf(stderr,GT_("Invalid file descriptor %d\n"),
|
|
+ ctl->passwordfd);
|
|
+ errflag++;
|
|
+ }
|
|
+ break;
|
|
case 't':
|
|
ctl->server.timeout = xatoi(optarg, &errflag);
|
|
if (ctl->server.timeout == 0)
|
|
--- a/rcfile_l.l
|
|
+++ b/rcfile_l.l
|
|
@@ -118,6 +118,8 @@ accept { return ACCEPT; }
|
|
reject { return REJECT_; }
|
|
|
|
user(name)? {SETSTATE(NAME); return USERNAME; }
|
|
+passwordfile { return PASSWORDFILE; }
|
|
+passwordfd { return PASSWORDFD; }
|
|
<INITIAL,NAME>pass(word)? {SETSTATE(NAME); return PASSWORD; }
|
|
folder(s)? { return FOLDER; }
|
|
smtp(host)? { return SMTPHOST; }
|
|
--- a/rcfile_y.y
|
|
+++ b/rcfile_y.y
|
|
@@ -63,6 +63,7 @@ extern char * yytext;
|
|
%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 PASSWORDFILE PASSWORDFD
|
|
%token SMTPADDRESS SMTPNAME SPAMRESPONSE PRECONNECT POSTCONNECT LIMIT WARNINGS
|
|
%token INTERFACE MONITOR PLUGIN PLUGOUT
|
|
%token IS HERE THERE TO MAP
|
|
@@ -307,6 +308,8 @@ user_option : TO mapping_list HERE
|
|
|
|
| IS STRING THERE {current.remotename = $2;}
|
|
| PASSWORD STRING {current.password = $2;}
|
|
+ | PASSWORDFILE STRING {current.passwordfile = $2;}
|
|
+ | PASSWORDFD NUMBER {current.passwordfd = NUM_VALUE_IN($2);}
|
|
| FOLDER folder_list
|
|
| SMTPHOST smtp_list
|
|
| FETCHDOMAINS fetch_list
|
|
@@ -505,6 +508,7 @@ static void reset_server(const char *nam
|
|
trailer = FALSE;
|
|
memset(¤t,'\0',sizeof(current));
|
|
current.smtp_socket = -1;
|
|
+ current.passwordfd = -1;
|
|
current.server.pollname = xstrdup(name);
|
|
current.server.skip = skip;
|
|
current.server.principal = (char *)NULL;
|
|
@@ -526,6 +530,7 @@ static void user_reset(void)
|
|
|
|
memset(¤t, '\0', sizeof(current));
|
|
current.smtp_socket = -1;
|
|
+ current.passwordfd = -1;
|
|
|
|
current.server = save;
|
|
}
|
|
@@ -546,6 +551,7 @@ struct query *hostalloc(struct query *in
|
|
{
|
|
memset(node, '\0', sizeof(struct query));
|
|
node->smtp_socket = -1;
|
|
+ node->passwordfd = -1;
|
|
}
|
|
|
|
/* append to end of list */
|