[PATCH] add option to read SASL password from command output

[PATCH] add option to read SASL password from command output

From: Karel Balej
Cc: pmnw, balejk
This is useful for instance for storing the password on the disk in
encrypted form and decrypting it on the fly whenever catgirl is run
without exposing it on the process command line.
---
 catgirl.1 | 12 +++++++++++-
 chat.c    | 19 +++++++++++++++++--
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/catgirl.1 b/catgirl.1
index 815eade48590..363cad8df821 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -214,7 +214,17 @@ with
 using SASL PLAIN.
 Leave
 .Ar pass
-blank to prompt for the password.
+blank to use
+.Cm sasl-pass-cmd
+if set or prompt for the password.
+.
+.It Fl A Ar cmd | Cm sasl-pass-cmd No = Ar cmd
+Use the first line of the output of
+.Ar cmd
+as password for SASL PLAIN. Only used when
+.Ar pass
+is not set in
+.Cm sasl-plain .
 .
 .It Fl c Ar path | Cm cert No = Ar path
 Load the TLS client certificate from
diff --git a/chat.c b/chat.c
index 672824027c6d..738b30bb0ead 100644
--- a/chat.c
+++ b/chat.c
@@ -239,6 +239,7 @@ int main(int argc, char *argv[]) {
 	char *pass = NULL;
 	const char *user = NULL;
 	const char *real = NULL;
+	char *plainPassCmd = NULL;
 
 	struct option options[] = {
 		{ .val = '!', .name = "insecure", no_argument },
@@ -270,6 +271,7 @@ int main(int argc, char *argv[]) {
 		{ .val = 'u', .name = "user", required_argument },
 		{ .val = 'v', .name = "debug", no_argument },
 		{ .val = 'w', .name = "pass", required_argument },
+		{ .val = 'A', .name = "sasl-pass-cmd", required_argument },
 		{0},
 	};
 	char opts[3 * ARRAY_LEN(options)];
@@ -317,6 +319,7 @@ int main(int argc, char *argv[]) {
 			break; case 'u': user = optarg;
 			break; case 'v': self.debug = true;
 			break; case 'w': pass = optarg;
+			break; case 'A': plainPassCmd = optarg;
 			break; default:  return EX_USAGE;
 		}
 	}
@@ -349,8 +352,20 @@ int main(int argc, char *argv[]) {
 	if (self.plainPass && !self.plainPass[0]) {
 		char *buf = malloc(512);
 		if (!buf) err(EX_OSERR, "malloc");
-		self.plainPass = readpassphrase("Account password: ", buf, 512, 0);
-		if (!self.plainPass) errx(EX_IOERR, "unable to read passphrase");
+		if (plainPassCmd) {
+			FILE *fp = popen(plainPassCmd, "r");
+			if (!fp)
+				errx(EX_OSERR, "unable to execute SASL passphrase command");
+			if (!fgets(buf, 512, fp))
+				errx(EX_SOFTWARE, "invalid SASL passphrase command output");
+			pclose(fp);
+			// Strip trailing newline, if present.
+			buf[strcspn(buf, "\n")] = 0;
+			self.plainPass = buf;
+		} else {
+			self.plainPass = readpassphrase("Account password: ", buf, 512, 0);
+			if (!self.plainPass) errx(EX_IOERR, "unable to read passphrase");
+		}
 	}
 
 	// Modes defined in RFC 1459:
-- 
2.44.0