util-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Asbjørn Sloth Tønnesen" <asbjorn@asbjorn.st>
To: util-linux@vger.kernel.org
Cc: "Asbjørn Sloth Tønnesen" <asbjorn@asbjorn.st>
Subject: [PATCH 2/2] script: add input log feature
Date: Sun, 19 Aug 2018 17:16:58 +0000	[thread overview]
Message-ID: <20180819171658.20285-2-asbjorn@asbjorn.st> (raw)
In-Reply-To: <20180819171658.20285-1-asbjorn@asbjorn.st>

When script is used to log usage of shell accounts, accessible by
random users (some of whom might be malicious).

This patch adds a new --input-log option, which makes script save
the input data to a file, in an extended timing format also containing
the data, thereby allowing the input to be reconstructed.

Signed-off-by: Asbjørn Sloth Tønnesen <asbjorn@asbjorn.st>
---
 bash-completion/script |  1 +
 term-utils/script.1    | 11 +++++++++-
 term-utils/script.c    | 58 +++++++++++++++++++++++++++++++++++++++++++++-----
 3 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/bash-completion/script b/bash-completion/script
index 57b91a9eb..c670dd5bd 100644
--- a/bash-completion/script
+++ b/bash-completion/script
@@ -25,6 +25,7 @@ _script_module()
 				--flush
 				--force
 				--quiet
+				--input-log
 				--output-limit
 				--timing=
 				--version
diff --git a/term-utils/script.1 b/term-utils/script.1
index 0fb4c4fd7..a55924f68 100644
--- a/term-utils/script.1
+++ b/term-utils/script.1
@@ -87,8 +87,17 @@ being done using `cat foo'.
 Allow the default output destination, i.e. the typescript file, to be a hard
 or symbolic link.  The command will follow a symbolic link.
 .TP
+\fB\-i, \fB\-\-input-log\fR \fIfile\fR
+Log input data to
+.IR file .
+This data contains three fields, separated by a space.  The first
+field indicates how much time elapsed since the previous input.  The second
+field indicates how many characters were input this time.  The third field is
+the input data. This information can be used to log input data, regardless of
+whether it's echoed back by the shell.  Beware this will log passwords in the input.
+.TP
 \fB\-o\fR, \fB\-\-output-limit\fR \fIsize\fR
-Limit the size of the typescript and timing files to
+Limit the combined size of the output files to
 .I size
 and stop the child process after this size is exceeded.  The calculated
 file size does not include the start and done messages that the
diff --git a/term-utils/script.c b/term-utils/script.c
index dfea87463..4ea6d3ccf 100644
--- a/term-utils/script.c
+++ b/term-utils/script.c
@@ -101,13 +101,17 @@ UL_DEBUG_DEFINE_MASKNAMES(script) = UL_DEBUG_EMPTY_MASKNAMES;
 struct script_control {
 	char *shell;		/* shell to be executed */
 	char *command;		/* command to be executed */
+	char *inputlogname;	/* input log file path */
+	FILE *inputlogfp;	/* input log file pointer */
 	char *fname;		/* output file path */
 	FILE *typescriptfp;	/* output file pointer */
 	char *tname;		/* timing file path */
 	FILE *timingfp;		/* timing file pointer */
 	uint64_t outsz;         /* current output file size */
 	uint64_t maxsz;		/* maximum output file size */
-	struct timeval oldtime;	/* previous write or command start time */
+	struct timeval
+		last_timing,	/* previous timing/input write or ... */
+		last_inputlog;	/* ... command start time */
 	int master;		/* pseudoterminal master file descriptor */
 	int slave;		/* pseudoterminal slave file descriptor */
 	int poll_timeout;	/* poll() timeout, used in end of execution */
@@ -124,6 +128,7 @@ struct script_control {
 	 flush:1,		/* flush after each write */
 	 quiet:1,		/* suppress most output */
 	 timing:1,		/* include timing file */
+	 inputlog:1,		/* write input log file */
 	 force:1,		/* write output to links */
 	 isterm:1,		/* is child process running as terminal */
 	 die:1;			/* terminate program */
@@ -172,6 +177,7 @@ static void __attribute__((__noreturn__)) usage(void)
 		" -e, --return                  return exit code of the child process\n"
 		" -f, --flush                   run flush after each write\n"
 		"     --force                   use output file even when it is a link\n"
+		" -i, --input-log=<file>        write input log to file\n"
 		" -o, --output-limit <size>     terminate if output files exceed size\n"
 		" -q, --quiet                   be quiet\n"
 		" -t[<file>], --timing[=<file>] output timing data to stderr or to FILE\n"
@@ -295,6 +301,8 @@ static void __attribute__((__noreturn__)) done_log(struct script_control *ctl, c
 		err(EXIT_FAILURE, "write failed: %s", ctl->tname);
 	if (ctl->typescriptfp && close_stream(ctl->typescriptfp) != 0)
 		err(EXIT_FAILURE, "write failed: %s", ctl->fname);
+	if (ctl->inputlogfp && close_stream(ctl->inputlogfp) != 0)
+		err(EXIT_FAILURE, "write failed: %s", ctl->inputlogname);
 
 	exit(ctl->rc_wanted ? childstatus : EXIT_SUCCESS);
 }
@@ -337,12 +345,12 @@ static void write_output(struct script_control *ctl, char *obuf,
 		DBG(IO, ul_debug("  writing timing info"));
 
 		gettime_monotonic(&now);
-		timersub(&now, &ctl->oldtime, &delta);
+		timersub(&now, &ctl->last_timing, &delta);
 		timing_bytes = fprintf(ctl->timingfp, "%ld.%06ld %zd\n",
 		        (long)delta.tv_sec, (long)delta.tv_usec, bytes);
 		if (ctl->flush)
 			fflush(ctl->timingfp);
-		ctl->oldtime = now;
+		ctl->last_timing = now;
 		if (timing_bytes < 0)
 			timing_bytes = 0;
 	}
@@ -373,6 +381,25 @@ static void write_output(struct script_control *ctl, char *obuf,
 static int write_to_shell(struct script_control *ctl,
 			  char *buf, size_t bufsz)
 {
+	if (ctl->inputlog && ctl->inputlogfp) {
+		struct timeval now, delta;
+		int inputlog_bytes = 0;
+		int bytes = bufsz;
+
+		DBG(IO, ul_debug("  writing input log"));
+
+		gettime_monotonic(&now);
+		timersub(&now, &ctl->last_inputlog, &delta);
+		inputlog_bytes = fprintf(ctl->inputlogfp, "%ld.%06ld %zd %.*s\n",
+		        (long)delta.tv_sec, (long)delta.tv_usec, bufsz, bytes, buf);
+		if (ctl->flush)
+			fflush(ctl->inputlogfp);
+		ctl->last_inputlog = now;
+
+		if (ctl->maxsz != 0)
+			ctl->outsz += inputlog_bytes;
+	}
+
 	return write_all(ctl->master, buf, bufsz);
 }
 
@@ -455,6 +482,9 @@ static void handle_io(struct script_control *ctl, int fd, int *eof)
 			warn(_("write failed"));
 			fail(ctl);
 		}
+
+		check_output_limit(ctl);
+
 		/* without sync write_output() will write both input &
 		 * shell output that looks like double echoing */
 		fdatasync(ctl->master);
@@ -520,6 +550,7 @@ static void handle_signal(struct script_control *ctl, int fd)
 static void do_io(struct script_control *ctl)
 {
 	int ret, eof = 0;
+	struct timeval now;
 	time_t tvec = script_time((time_t *)NULL);
 	enum {
 		POLLFD_SIGNAL = 0,
@@ -551,11 +582,23 @@ static void do_io(struct script_control *ctl)
 		}
 	}
 
+	if (ctl->inputlog) {
+		const char *ilname = ctl->inputlogname;
+
+		if (!(ctl->inputlogfp = fopen(ilname, "w" UL_CLOEXECSTR))) {
+			restore_tty(ctl, TCSANOW);
+			warn(_("cannot open %s"), ilname);
+			fail(ctl);
+		}
+	}
+
 
 	if (ctl->typescriptfp)
 		typescript_message_start(ctl, &tvec);
 
-	gettime_monotonic(&ctl->oldtime);
+	gettime_monotonic(&now);
+	ctl->last_timing = now;
+	ctl->last_inputlog = now;
 
 	while (!ctl->die) {
 		size_t i;
@@ -775,6 +818,7 @@ int main(int argc, char **argv)
 		{"return", no_argument, NULL, 'e'},
 		{"flush", no_argument, NULL, 'f'},
 		{"force", no_argument, NULL, FORCE_OPTION,},
+		{"input-log", required_argument, NULL, 'i'},
 		{"output-limit", required_argument, NULL, 'o'},
 		{"quiet", no_argument, NULL, 'q'},
 		{"timing", optional_argument, NULL, 't'},
@@ -798,7 +842,7 @@ int main(int argc, char **argv)
 
 	script_init_debug();
 
-	while ((ch = getopt_long(argc, argv, "ac:efo:qt::Vh", longopts, NULL)) != -1)
+	while ((ch = getopt_long(argc, argv, "ac:efi:o:qt::Vh", longopts, NULL)) != -1)
 		switch (ch) {
 		case 'a':
 			ctl.append = 1;
@@ -815,6 +859,10 @@ int main(int argc, char **argv)
 		case FORCE_OPTION:
 			ctl.force = 1;
 			break;
+		case 'i':
+			ctl.inputlog = 1;
+			ctl.inputlogname = optarg;
+			break;
 		case 'o':
 			ctl.maxsz = strtosize_or_err(optarg, _("failed to parse output limit size"));
 			break;
-- 
2.16.1

  reply	other threads:[~2018-08-19 20:36 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-19 17:16 [PATCH 1/2] script: move output limit check to dedicated function Asbjørn Sloth Tønnesen
2018-08-19 17:16 ` Asbjørn Sloth Tønnesen [this message]
2018-08-20  8:46   ` [PATCH 2/2] script: add input log feature Karel Zak
2018-09-25 20:44     ` Asbjørn Sloth Tønnesen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180819171658.20285-2-asbjorn@asbjorn.st \
    --to=asbjorn@asbjorn.st \
    --cc=util-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).