Subject: port-i386/8196: i386 boot loader enhancement
To: None <gnats-bugs@gnats.netbsd.org>
From: None <thesing@cs.uni-sb.de>
List: netbsd-bugs
Date: 08/12/1999 09:24:13
>Number:         8196
>Category:       port-i386
>Synopsis:       boot loader enhancement
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-i386-maintainer (NetBSD/i386 Portmaster)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Thu Aug 12 09:20:00 1999
>Last-Modified:
>Originator:     Stephan Thesing
>Organization:
=  Tel.: +49-681-302-5571          = Universitaet des Saarlandes =
=  Fax.: +49-681-302-3065          = Postfach 15 11 50           =
=  Compiler Research Group         = 66041 Saarbruecken          =
=  FB 14 - Informatik              = GERMANY                     =
>Release:        NetBSD-current 080899
>Environment:
	
System: NetBSD gargoyle.cs.uni-sb.de 1.4J NetBSD 1.4J (Gargoyle) #0: Sun Aug 8 21:39:47 CEST 1999 thesing@gargoyle.cs.uni-sb.de:/usr/src/sys/arch/i386/compile/Gargoyle i386


>Description:
	The bootblock code for i386 allows any user at the console to
 	boot from another medium (e.g. floppy) or into single-user 
        (or debug-) mode. This enables anybody to gain 'root'-like access
	to the system, which may be undesirable.

>How-To-Repeat:
	use the boot menu to boot from floppy, instead of wd0:-)
	
>Fix:
	Attached is a patch for stand/biosboot, stand/installboot which
	- allows to set the timeout, after which the kernel is automatically   
          booted                                                               
        - allows a password to be set, which must be entered to                
          access the boot menu.

	The new options to 'installboot' are
        -t timeout
        -p password
	(/usr/mdec/installboot -t 0 /usr/mdec/biosboot.sym /dev/rwd0a
         e.g. will set the timeout to 0 seconds, loading the kernel
         directly, without the possibility to enter the boot menu)

        Changes made to the boot loader code:
        - /usr/src/sys/arch/i386/stand/biosboot/main.c:
	  Use a timeout from variable 'bootimeout' rather than the constant
	  TIMEOUT 
          Ask for the password (if any was set) before giving access to
	  the boot menu (asks three times before returning to boot procedure)
	  Read a password without displaying the actual characters :-) 
          (getspwd)
	- /usr/src/sys/arch/i386/stand/lib/crt/bootsect/fraglist.S:
	  Reserve space for the bootimeout and bootpasswd variables
	- /usr/src/sys/arch/i386/stand/lib/pcio.c: 
	  Handle countdown of boot time for timeouts >10 correctly 
	  (emit enough 'backspace' characters to erase the previous
	   count)
        
	BUGS/DEFICIENCIES:
 	Doesn't do much error checking in installboot.c.
	Doen't encrypt the password. Everybody with read access to the boot
	blocks can see it in clear text; 
	Duplicates code from /usr/src/sys/lib/libsa/gets.c to implement
	getspwd().

--- usr/src/sys/arch/i386/stand/biosboot/main.c.orig	Wed Aug 11 17:02:50 1999
+++ usr/src/sys/arch/i386/stand/biosboot/main.c	Thu Aug 12 17:31:33 1999
@@ -69,6 +69,10 @@
 
 #define TIMEOUT 5
 
+extern int boottimeout;
+
+extern char  bootpasswd[80];
+
 static char *default_devname;
 static int default_unit, default_partition;
 static char *default_filename;
@@ -77,6 +81,8 @@
 void bootit __P((const char *, int, int));
 void print_banner __P((void));
 void main __P((void));
+int  checkpasswd __P((void));
+void getspwd __P((char *, int));
 
 void	command_help __P((char *));
 void	command_ls __P((char *));
@@ -249,10 +255,12 @@
 		printf("booting %s - starting in ",
 		       sprint_bootsel(names[currname]));
 
-		c = awaitkey(TIMEOUT, 1);
+		c = awaitkey(boottimeout, 1);
 		if ((c != '\r') && (c != '\n') && (c != '\0')) {
-			printf("type \"?\" or \"help\" for help.\n");
-			bootmenu(); /* does not return */
+			if (checkpasswd()) {
+				printf("type \"?\" or \"help\" for help.\n");
+				bootmenu(); /* does not return */
+			}
 		}
 
 		/*
@@ -348,3 +356,89 @@
 	strncpy(savedevname, devname, MAXDEVNAME + 1);
 	default_devname = savedevname;
 }
+
+
+int
+checkpasswd(void)
+{
+	char password[80];
+	char *c=password;
+	int j;
+
+	/* if no password, return */
+	if (bootpasswd[0]==0)
+		return 1;
+
+
+	/* try to obtain password 3 times */
+        for (j=0;j<3;j++) {
+		printf("Password: ");
+
+		getspwd(c,79);
+	
+		if (!strcmp(password,bootpasswd))
+			return 1;
+
+		printf("Wrong password\n");
+	}
+
+	return 0;
+}
+
+void
+getspwd(buf, size)
+char *buf;
+int size;
+{
+        register int c;
+        register char *lp;
+	register int sz=0;
+
+        for (lp = buf;;)
+                switch (c = getchar() & 0177) {
+                case '\n':
+                case '\r':
+                        *lp = '\0';
+                        putchar('\n');
+                        return;
+                case '\b':
+                case '\177':
+                        if (lp > buf) {
+                                lp--;
+                                putchar('\b');
+                                putchar(' ');
+                                putchar('\b');
+                        }
+                        break;
+#if HASH_ERASE
+                case '#':
+                        if (lp > buf)
+                                --lp;
+                        break;
+#endif
+                case 'r'&037: {
+                        register char *p;
+
+                        putchar('\n');
+                        for (p = buf; p < lp; ++p)
+                                putchar('*');
+                        break;
+                }
+#if AT_ERASE
+                case '@':
+#endif
+                case 'u'&037:
+                case 'w'&037:
+                        lp = buf;
+                        putchar('\n');
+                        break;
+                default:
+			if (sz<size) {
+				sz++;
+	                        *lp++ = c;
+	                        putchar('*');
+			}
+                }
+        /*NOTREACHED*/
+}
+
--- usr/src/sys/arch/i386/stand/installboot/installboot.c.orig	Thu Aug 12 13:02:50 1999
+++ usr/src/sys/arch/i386/stand/installboot/installboot.c	Thu Aug 12 17:36:47 1999
@@ -77,11 +77,17 @@
 
 struct nlist nl[] = {
 #define X_fraglist	0
+#define X_boottimeout	1
+#define X_bootpasswd	2
 #ifdef __ELF__
 	{{"fraglist"}},
+	{{"boottimeout"}},
+	{{"bootpasswd"}},
 #define SYM_TYPE N_EXT
 #else
 	{{"_fraglist"}},
+        {{"_boottimeout"}},
+        {{"_bootpasswd"}},
 #define SYM_TYPE (N_TEXT|N_EXT)
 #endif
 	{{NULL}}
@@ -414,7 +420,7 @@
 usage()
 {
 	(void) fprintf(stderr,
-	       "usage: installboot [-b bno] [-n] [-v] [-f] <boot> <device>\n");
+	       "usage: installboot [-b bno] [-n] [-v] [-f] [-t timeout] [-p passwd] <boot> <device>\n");
 	exit(1);
 }
 
@@ -435,9 +441,13 @@
 	char *bootblkname = DEFBBLKNAME;
 	int nowrite = 0;
 	int allok = 0;
+	int timeout=-1;
+	char password[80];
 	ino_t (*save_func) __P((char *, char *, char *, unsigned int));
 
-	while ((c = getopt(argc, argv, "b:vnf")) != -1) {
+	password[0]=0;
+
+	while ((c = getopt(argc, argv, "b:vnft:p:")) != -1) {
 		switch (c) {
 		case 'b':
 			/* generic override, supply starting block # */
@@ -456,6 +466,20 @@
 			/* assume zero offset if no disklabel */
 			forceifnolabel = 1;
 			break;
+		case 't':
+			/* boot timeout */
+			timeout=atoi(optarg);
+			if (timeout<0) {
+				fprintf(stderr,"not using timeout\n");
+				timeout=-1;
+			} 
+			break;
+		case 'p':
+			/* password */
+			if (strlen(optarg)<4||strlen(optarg)>79) 
+				errx(1, "illegal password");	
+			strcpy(password,optarg);
+			break;
 		default:
 			usage();
 		}
@@ -471,6 +495,12 @@
 
 	fraglist->numentries = 0;
 
+	if (timeout>=0)
+	  *((int *)(bp+nl[X_boottimeout].n_value))=timeout;
+
+	if (password[0])
+	  strcpy(bp+nl[X_bootpasswd].n_value,password);
+
 	if (conblockmode)
 		save_func = save_passthru;
 	else
@@ -512,9 +542,16 @@
 		}
 		bsdoffs = dl.d_partitions[p - 'a'].p_offset;
 	}
-	if (verbose)
+	if (verbose) {
 		(void) fprintf(stderr, "BSD partition starts at sector %d\n",
 			       bsdoffs);
+                if (timeout>=0)
+                        (void) fprintf(stderr, "timeout after %d seconds\n",
+                                timeout);
+                if (password[0])
+                        (void) fprintf(stderr, "password is %s\n", password);
+        
+	}
 
 	/*
          * add offset of BSD partition to fraglist entries
--- usr/src/sys/arch/i386/stand/lib/crt/bootsect/fraglist.S.orig	Wed Aug 11 17:39:56 1999
+++ usr/src/sys/arch/i386/stand/lib/crt/bootsect/fraglist.S	Wed Aug 11 17:41:16 1999
@@ -49,3 +49,12 @@
 	.long 0
 	.long MAXFLENTRIES
 	. = . + 2 * MAXFLENTRIES * 4
+
+	.globl _C_LABEL(boottimeout)
+_C_LABEL(boottimeout):
+	.long 5
+
+	.globl _C_LABEL(bootpasswd)
+_C_LABEL(bootpasswd):
+	.long 0
+	. = . + 76
--- usr/src/sys/arch/i386/stand/lib/pcio.c.orig	Tue Jun 29 02:02:36 1999
+++ usr/src/sys/arch/i386/stand/lib/pcio.c	Thu Aug 12 15:21:08 1999
@@ -302,8 +302,16 @@
 	i = timeout * POLL_FREQ;
 
 	while (i) {
-		if (tell && (i % POLL_FREQ) == 0)
-			printf("%d\b", i/POLL_FREQ);
+		if (tell && (i % POLL_FREQ) == 0) {
+			char buff[10];
+			int j,k;
+			strcpy(buff,"\b\b\b\b\b\b\b\b\b");
+			for (j=1,k=i/POLL_FREQ; k>0; j++, k/=10);
+			if (j>9) 
+				j=9;
+			buff[j]=0;
+			printf("%d %s", i/POLL_FREQ,buff);
+		}
 		if (iskey()) {
 			/* flush input buffer */
 			while (iskey())
>Audit-Trail:
>Unformatted: