[PATCH 6/8] Add an autologin feature

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

 



Add an autologin feature to agetty, that is that a user can be
automatically logged in.  For this the options of for the
login program has to used.  Make it possible to pass-through
options to the login program which requires a security check.

Signed-off-by: Werner Fink <werner@xxxxxxx>
---
 term-utils/agetty.8 |   29 ++++++++
 term-utils/agetty.c |  197 ++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 202 insertions(+), 24 deletions(-)

diff --git a/term-utils/agetty.8 b/term-utils/agetty.8
index d30f7ce..ddd8196 100644
--- a/term-utils/agetty.8
+++ b/term-utils/agetty.8
@@ -4,6 +4,7 @@ agetty \- alternative Linux getty
 
 .SH SYNOPSIS
 .BR "agetty " [\-8chiLmnsUw]
+.RI "[-a " user ]
 .RI "[-f " issue_file ]
 .RI "[-H " login_host ]
 .RI "[-I " init ]
@@ -85,6 +86,15 @@ whatever init(8) may have set, and is inherited by login and the shell.
 \-8, \-\-8bits
 Assume that the tty is 8-bit clean, hence disable parity detection.
 .TP
+\-a, \-\-autologin \fIusername\fP
+Log the specified user automatically in without asking for a login
+name and password. Check the -f option from
+\fB/bin/login\fP for this.
+.TP
+\-\-loginpause
+Wait for any key before dropping to the login prompt.  Can be combined
+with \fB\-\-autologin\fP to save memory by lazily spawning shells.
+.TP
 \-c, \-\-noreset
 Don't reset terminal cflags (control modes). See \fItermios(3)\fP for more
 details.
@@ -125,6 +135,12 @@ This allows the use of a non-standard login program (for example,
 one that asks for a dial-up password or that uses a different
 password file).
 .TP
+\-\-logopts \fI"login_options"\fP
+Options  that  are passed to the login program.  \\u is replaced
+by the login name. Defaults to "-- \\u", which is suitable for
+\fB/bin/login\fP.  Please read the SECURITY NOTICE below if
+you want to use this.
+.TP
 \-L, \-\-local-line
 Force the line to be a local line with no need for carrier detect. This can
 be useful when you have a locally attached terminal where the serial line
@@ -207,6 +223,19 @@ dis-connection and turn on auto-answer after 1 ring.)
 .ti +5
 /sbin/agetty \-w \-I 'ATE0Q1&D2&C1S0=1\\015' 115200 ttyS1
 
+.SH SECURITY NOTICE
+If you use the \fB\-\-login\fP and \fB\-\-logopts\fP options, be aware
+that a malicious user may try to enter lognames with embedded options,
+which then get passed to the used login  program.  Agetty does check
+for a leading - and makes sure the logname gets passed as one parameter
+(so embedded spaces will not create yet another parameter), but depending
+on how the login binary parses the command line that might not be sufficient.
+Check that the used login program  can  not  be  abused this way.
+.PP
+Some  programs use -- to indicate that the rest of the commandline should
+not be interpreted as options. Use this feature if available by passing -- before
+the username gets passed by \\u.
+
 .SH ISSUE ESCAPES
 The issue-file (\fI/etc/issue\fP or the file set with the \-f option)
 may contain certain escape codes to display the system name, date and
diff --git a/term-utils/agetty.c b/term-utils/agetty.c
index 2bee594..4c76d96 100644
--- a/term-utils/agetty.c
+++ b/term-utils/agetty.c
@@ -95,6 +95,7 @@
 
 /* Login prompt. */
 #define LOGIN " login: "
+#define ARRAY_SIZE_MAX 16
 
 /* Some shorthands for control characters. */
 #define CTL(x)		(x ^ 0100)	/* Assumes ASCII dialect */
@@ -132,7 +133,9 @@
 struct options {
 	int flags;			/* toggle switches, see below */
 	int timeout;			/* time-out period */
+	char *autolog;			/* login the user automatically */
 	char *login;			/* login program */
+	char *logopt;			/* options for login program */
 	char *tty;			/* name of tty */
 	char *vcline;			/* line of virtual console */
 	char *term;			/* terminal type */
@@ -157,6 +160,7 @@ struct options {
 #define F_VCONSOLE	(1<<12)	/* This is a virtual console */
 #define F_NOHANGUP	(1<<13)	/* No not call vhangup(2) */
 #define F_UTF8		(1<<14)	/* We can do UTF8 */
+#define F_LOGINPAUSE	(1<<15)	/* Wait for any key before dropping login prompt */
 
 /* Storage for things detected while the login name was read. */
 struct chardata {
@@ -242,6 +246,9 @@ static void usage(FILE * out) __attribute__((__noreturn__));
 static void error(const char *, ...) __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2)));
 static void warn (const char *, ...) __attribute__((__format__(printf, 1, 2)));
 static ssize_t safe_write (int fd, const char *buffer, size_t count);
+static void checkname (const char* nm);
+static void replacename (char** arr, const char* nm);
+static void mkarray (char** arr, char* str);
 
 /* Fake hostname for ut_host specified on command line. */
 static char *fakehost;
@@ -256,23 +263,21 @@ FILE *dbf;
 int main(int argc, char **argv)
 {
 	char *logname = NULL;		/* login name, given to /bin/login */
+	char  logcmd[NAME_MAX+1];
+	char *logarr[ARRAY_SIZE_MAX];
 	struct chardata chardata;	/* will be set by get_logname() */
 	struct termios termios;		/* terminal mode bits */
-	static struct options options = {
-		F_ISSUE,	/* show /etc/issue (SYSV_STYLE) */
-		0,		/* no timeout */
-		_PATH_LOGIN,	/* default login program */
-		"tty1",		/* default tty line */
-		0,		/* line of virtual console */
-		DEFAULT_VCTERM,	/* terminal type */
-		"",		/* modem init string */
-		ISSUE,		/* default issue file */
-		0, 		/* no baud rates known yet */
-		{ 0 }
-	};
+	static struct options options;
 	struct sigaction sa, sa_hup, sa_quit, sa_int;
 	sigset_t set;
 
+	options.flags  =  F_ISSUE;		/* show /etc/issue (SYSV_STYLE) */
+	options.login  =  _PATH_LOGIN;		/* default login program */
+	options.logopt = "-- \\u";		/* escape for user name */
+	options.tty    = "tty1";		/* default tty line */
+	options.term   =  DEFAULT_VCTERM;	/* terminal type */
+	options.issue  =  ISSUE;		/* default issue file */
+
 	setlocale(LC_ALL, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
 	textdomain(PACKAGE);
@@ -315,7 +320,8 @@ int main(int argc, char **argv)
 	termio_init(&options, &termios);
 
 	/* Write the modem init string and DO NOT flush the buffers. */
-	if ((options.flags & (F_VCONSOLE|F_INITSTRING)) == F_INITSTRING) {
+	if ((options.flags & (F_VCONSOLE|F_INITSTRING)) == F_INITSTRING &&
+	    options.initstring && *options.initstring != '\0') {
 		debug("writing init string\n");
 		safe_write(STDIN_FILENO, options.initstring,
 			   strlen(options.initstring));
@@ -353,13 +359,22 @@ int main(int argc, char **argv)
 
 	chardata = init_chardata;
 	if ((options.flags & F_NOPROMPT) == 0) {
-		/* Read the login name. */
-		debug("reading login name\n");
-		while ((logname =
-			get_logname(&options, &termios, &chardata)) == 0)
-			if ((options.flags & F_VCONSOLE) == 0) {
-				next_speed(&options, &termios);
-			}
+
+		if (options.autolog) {
+			/* Do the auto login */
+			debug("doing auto login\n");
+			do_prompt(&options, &termios);
+			printf ("%s%s (automatic login)\n", LOGIN, options.autolog);
+			logname = options.autolog;
+			options.logopt = "-f \\u";
+		} else {
+			/* Read the login name. */
+			debug("reading login name\n");
+			while ((logname = get_logname(&options, &termios, &chardata)) == 0)
+				if ((options.flags & F_VCONSOLE) == 0) {
+					next_speed(&options, &termios);
+				}
+		}
 	}
 
 	/* Disable timer. */
@@ -374,8 +389,19 @@ int main(int argc, char **argv)
 		safe_write(STDOUT_FILENO, "\r\n", 1);
 	}
 
+	sigaction (SIGQUIT, &sa_quit, NULL);
+	sigaction (SIGINT, &sa_int, NULL);
+
+	strncpy(logcmd, options.login, NAME_MAX);
+	strncat(logcmd, " ", NAME_MAX - strlen(logcmd));
+	strncat(logcmd, options.logopt, NAME_MAX - strlen(logcmd));
+
+	checkname(logname);
+	mkarray(logarr, logcmd);
+	replacename(logarr, logname);
+
 	/* Let the login program take care of password validation. */
-	execl(options.login, options.login, "--", logname, NULL);
+	execv(options.login, logarr);
 	warn(_("%s: can't exec %s: %m"), options.tty, options.login);
 	sleep(5);
 	exit(EXIT_FAILURE);
@@ -394,7 +420,9 @@ static void parse_args(int argc, char **argv, struct options *op)
 	};
 	const struct option longopts[] = {
 		{  "nohangup",	     no_argument,  &nipple,  F_NOHANGUP	    },
+		{  "loginpause",     no_argument,  &nipple,  F_LOGINPAUSE   },
 		{  "8bits",	     no_argument,	 0,  '8'  },
+		{  "autologin",      required_argument,  0,  'a'  },
 		{  "noreset",	     no_argument,	 0,  'c'  },
 		{  "issue-file",     required_argument,  0,  'f'  },
 		{  "flow-control",   no_argument,	 0,  'h'  },
@@ -414,7 +442,7 @@ static void parse_args(int argc, char **argv, struct options *op)
 		{ NULL, 0, 0, 0 }
 	};
 
-	while ((c = getopt_long(argc, argv, "8cf:hH:iI:l:Lmnst:Uw", longopts,
+	while ((c = getopt_long(argc, argv, "8a:cf:hH:iI:l:Lmnst:Uw", longopts,
 			    &index)) != -1) {
 		switch (c) {
 		case 0:
@@ -423,6 +451,9 @@ static void parse_args(int argc, char **argv, struct options *op)
 		case '8':
 			op->flags |= F_EIGHTBITS;
 			break;
+		case 'a':
+			op->autolog = optarg;
+			break;
 		case 'c':
 			op->flags |= F_KEEPCFLAGS;
 			break;
@@ -1064,13 +1095,60 @@ static void do_prompt(struct options *op, struct termios *tp)
 		fclose(fd);
 	}
 #endif				/* ISSUE */
+	if (op->flags & F_LOGINPAUSE) {
+		puts("[press ENTER to login]");
+		getc(stdin);
+	}
+#ifdef KDGKBLED
+	if (op->autolog == (char*)0 && (op->flags & F_VCONSOLE)) {
+		int kb = 0, nl = 0;
+		struct stat st;
+		if (stat("/var/run/numlock-on", &st) == 0)
+			nl = 1;
+		if (ioctl(STDIN_FILENO, KDGKBLED, &kb) == 0) {
+			char warn[128];
+			off_t len = 0;
+ 
+			if (nl && (kb & 0x02) == 0) {
+				strcpy(&warn[0], "Num Lock off");
+				len += 12;
+			} else if (nl == 0 && (kb & 2) && (kb & 0x20) == 0) {
+				strcpy(&warn[0], "Num Lock on");
+				len += 11;
+			}
+ 
+			if ((kb & 0x04) && (kb & 0x40) == 0) {
+				if (len) {
+					strcpy(&warn[len], ", ");
+					len += 2;
+				}
+				strcpy(&warn[len], "Caps Lock on");
+				len += 12;
+			}
+ 
+			if ((kb & 0x01) && (kb & 0x10) == 0) {
+				if (len) {
+					strcpy(&warn[len], ", ");
+					len += 2;
+				}
+				strcpy(&warn[len], "Scroll Lock on");
+				len += 14;
+			}
+ 
+			if (len)
+				printf ("Hint: %s\n\n", warn);
+		}
+	}
+#endif
 	{
 		char hn[MAXHOSTNAMELEN + 1];
 		if (gethostname(hn, sizeof(hn)) == 0)
 			safe_write(STDIN_FILENO, hn, strlen(hn));
 	}
-	/* Always show login prompt. */
-	safe_write(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
+
+	if (op->autolog == (char*)0)
+		/* Always show login prompt. */
+		safe_write(STDOUT_FILENO, LOGIN, sizeof(LOGIN) - 1);
 }
 
 /* Select next baud rate. */
@@ -1357,6 +1435,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
 
 	fprintf(out, _("\nOptions:\n"
 		       " -8, --8bits                assume 8-bit tty\n"
+		       " -a, --autologin USER       login the specified user automatically\n"
 		       " -c, --noreset              do not reset control mode\n"
 		       " -f, --issue-file FILE      display issue file\n"
 		       " -h, --flow-control         enable hardware flow control\n"
@@ -1371,6 +1450,8 @@ static void __attribute__ ((__noreturn__)) usage(FILE * out)
 		       " -t, --timeout NUMBER       login process timeout\n"
 		       " -U, --detect-case          detect uppercase terminal\n"
 		       " -w, --wait-cr              wait carriage-return\n"
+		       "     --nohangup             do not call vhangup() at start\n"
+		       "     --nohangup             do not call vhangup() at start\n"
 		       "     --version              output version information and exit\n"
 		       "     --help                 display this help and exit\n\n"));
 
@@ -1635,3 +1716,71 @@ static void init_special_char(char* arg, struct options *op)
 	}
 	*q = '\0';
 }
+
+/*
+ * Do not allow the user to pass an option as a user name
+ * To be more safe: Use `--' to make sure the rest is
+ * interpreted as non-options by the program, if it supports it.
+ */
+static void checkname(const char* nm)
+{
+	const char *p = nm;
+	if (!nm)
+	goto err;
+	if (strlen (nm) > 42)
+	goto err;
+	while (isspace (*p))
+	p++;
+	if (*p == '-')
+	goto err;
+	return;
+err:
+	errno = EPERM;
+	error ("checkname: %m");
+}
+
+static void replacename(char** arr, const char* nm)
+{
+	const char *p;
+	char *tmp;
+	while ((p = *arr)) {
+	const char *p1 = p;
+	while (*p1) {
+		if (memcmp (p1, "\\u", 2) == 0) {
+			tmp = malloc (strlen (p) + strlen (nm));
+			if (!tmp)
+				error ("replacename: %m");
+			if (p1 != p)
+				memcpy (tmp, p, (p1-p));
+			*(tmp + (p1-p)) = 0;
+			strcat (tmp, nm);
+			strcat (tmp, p1+2);
+			*arr = tmp;
+		}
+		p1++;
+	}
+	arr++;
+}
+}
+
+static void mkarray(char** arr, char* str)
+{
+	char* p = str;
+	char* start = p;
+	int i = 0;
+
+	while (*p && i < (ARRAY_SIZE_MAX - 2)) {
+	if (isspace (*p)) {
+		*p = 0;
+		while (isspace (*++p))
+			;
+		if (*p) {
+			arr[i++] = start;
+			start = p;
+		}
+	} else
+		p++;
+	}
+	arr[i++] = start;
+	arr[i++] = (char*)0;
+}
-- 
1.6.0.2

--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[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