[PATCH 2/2] script: add input log feature

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxx>
---
 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




[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux