File fetchmail-add-passwordfile-and-passwordfd-options.patch of Package fetchmail

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(-)

Index: fetchmail-6.5.1/fetchmail.c
===================================================================
--- fetchmail-6.5.1.orig/fetchmail.c
+++ fetchmail-6.5.1/fetchmail.c
@@ -471,7 +471,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)
 	    {
 		const netrc_entry *p;
 
@@ -649,8 +649,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);
@@ -1046,6 +1119,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);
@@ -1112,6 +1189,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.idle_timeout = CLIENT_IDLE_TIMEOUT;
Index: fetchmail-6.5.1/fetchmail.h
===================================================================
--- fetchmail-6.5.1.orig/fetchmail.h
+++ fetchmail-6.5.1/fetchmail.h
@@ -312,6 +312,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 */
Index: fetchmail-6.5.1/fetchmail.man
===================================================================
--- fetchmail-6.5.1.orig/fetchmail.man
+++ fetchmail-6.5.1/fetchmail.man
@@ -1056,6 +1056,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
@@ -1148,7 +1179,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"
@@ -2100,6 +2132,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}
Index: fetchmail-6.5.1/options.c
===================================================================
--- fetchmail-6.5.1.orig/options.c
+++ fetchmail-6.5.1/options.c
@@ -29,6 +29,8 @@ enum {
     LA_POSTMASTER,
     LA_NOBOUNCE,
     LA_AUTH,
+    LA_PASSWORDFILE,
+    LA_PASSWORDFD,
     LA_FETCHDOMAINS,
     LA_BSMTP,
     LA_LMTP,
@@ -98,6 +100,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' },
@@ -231,6 +235,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,
@@ -402,6 +407,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)
Index: fetchmail-6.5.1/rcfile_l.l
===================================================================
--- fetchmail-6.5.1.orig/rcfile_l.l
+++ fetchmail-6.5.1/rcfile_l.l
@@ -116,6 +116,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; }
Index: fetchmail-6.5.1/rcfile_y.y
===================================================================
--- fetchmail-6.5.1.orig/rcfile_y.y
+++ fetchmail-6.5.1/rcfile_y.y
@@ -62,6 +62,7 @@ void yyerror (const char *s)
 %token DEFAULTS POLL SKIP VIA AKA LOCALDOMAINS PROTOCOL
 %token AUTHENTICATE TIMEOUT IDLETIMEOUT 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
@@ -314,6 +315,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
@@ -495,6 +498,7 @@ static void reset_server(const char *nam
     trailer = FALSE;
     memset(&current,'\0',sizeof(current));
     current.smtp_socket = -1;
+    current.passwordfd = -1;
     current.server.pollname = xstrdup(name);
     current.server.skip = skip;
 }
@@ -515,6 +519,7 @@ static void user_reset(void)
 
     memset(&current, '\0', sizeof(current));
     current.smtp_socket = -1;
+    current.passwordfd = -1;
 
     current.server = save;
 }
@@ -535,6 +540,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 */
openSUSE Build Service is sponsored by