[comp.sys.sun] "Best Way" to inhibit halting of workstations

miker@central.sun.com (Mike Raffety) (04/21/89)

To achieve a secure single-user mode under SunOS 3.X, there's a couple of
ways to do it; the documented, easy, not-quite-perfect way, and the
foolproof, you-need-Unix-source way:

1.  At the tail end of the init(8) man page, in the BUGS section, there's
a documented method.  Unfortunately, I believe you can get around this
simply by INTerrupting at the right moment.  Here's the BUGS section:

------
When coming up single-user, the system does not require the super-user
to log in.  To force a login when running single-user, add the line:

	login root

to the file /.profile.
------

2.  The foolproof way requires Unix source; change init so that when it
comes up single-user, it execs login, instead of sh:

------
fine% scc sccsdiff -r1.1 -r1.2 init.c
23a24
> char  login[] = "/bin/login";
227c228,230
<                       execl(shell, minus, (char *)0);
---
>                       write(2, "Single-user mode...", 20);
>                       execl(login, login, (char *)0);
> /*                    execl(shell, minus, (char *)0);    MVR  */
fine% 
------

The interesting thing about BOTH of these is that login will time out
after 60 seconds, and the machine will continue to come up multi-user.
Note that fsck will NOT have been run when it comes up, since it
originally came up single-user.

brent@uunet.uu.net (Brent Chapman) (04/21/89)

# What is the best way to stop non-staff personel from halting a workstation
# and then booting it up in single user mode as root?
# 
# I've seen numerous suggestions posted here.  I would like to know what
# method has proved the most secure and feature free in practice.

If you're running 3.x, you can use the following bit of code.  It is
executed instead of /bin/sh by /etc/init when going into single user mode,
and prompts the user for the root password; if the user supplies the
correct password, then /bin/sh is invoked as usual, but otherwise (after 3
failed password attempts) the machine is halted. 

Read the comments very carefully and think hard before proceeding; this
"fix" involves a binary patch to /etc/init, and if you make a mistake,
recovery could be painful.  If you make a mistake and break your
/etc/init, you will _not_ be able to boot from your normal root, not even
single user.  If this happens to you, you'll have to boot from an
alternate root (from tape, or from a server somewhere) then mount your
"real" root and restore the old /etc/init; this can be a real hassle, and
can ruin your whole day. 

I'm not trying to disuade anyone from using the program (it's fairly
simple, and closes a serious security hole in 3.x systems); I just want
everyone to be damn sure they understand how tricky it is to install, and
what the consequences can be if a mistake is made during installation.

Anyway, with all those caveats, the code is appended to the end of this
message.


-Brent
--
Brent Chapman					Capital Market Technology, Inc.
Computer Operations Manager			1995 University Ave., Suite 390
brent@capmkt.com				Berkeley, CA  94704
{cogsci,lll-tis,uunet}!capmkt!brent		Phone:  415/540-6400

======== file "sq.c" follows ========

/*
 *  This program forms a small layer between init and /bin/sh to prompt
 *  for the root password before going to single user mode;
 *
 *  to augment security by preventing users acquiring root privileges
 *  by shutting down the system with L1-a and consequently starting
 *  the system single-user;
 *
 *  made & installed by the following (you must be root to do the install):
 *
 *	cc -O -o sq sq.c
 *	install -m 0700 -o root sq /bin/sq
 *
 *  this program should be in /bin/sq and then you can patch /etc/init
 *  at address 0x2000c to exec /bin/sq instead of /bin/sh.
 *
 *  Become super-user and issue the following commands.  This patch to
 *  /etc/init makes it start /bin/sq rather than /bin/sh.  /bin/sq starts
 *  /bin/sh if and only if the user enters the correct root password.  You
 *  must do the mv & cp before beginning because the current /etc/init file
 *  is "busy" (because the program is running) and can't be modified.  First
 *  create the new copy of init, then check to make sure that your 'init' is
 *  the same as mine:
 *
 *	mv /etc/init /etc/init.old
 *	cp /etc/init.old /etc/init
 *      echo "0x2000c?s" | adb /etc/init
 *
 *  If you don't get the following response, STOP HERE, because the patch
 *  isn't going to work (if you feel comfortable doing so, you could try
 *  to figure out where "/bin/sh" is in your /etc/init, and modify the
 *  patch below accordingly).  The response you should get to the last
 *  command above is:
 *
 *      0x2000c:        /sh
 *
 *  Like I said, if that's not the response you get, then the following patch
 *  won't work, and is likely to do strange and wonderful things to your
 *  /etc/init.  Anyway, if that _is_ what you get, the 0x2000c _is_ the righ
 *  location, and you can do the following to change init's exec of "/bin/sh"
 *  to "/bin/sq".  Note that adb is _very_ case sensitive; the capital W is
 *  quite significant...
 *
 *  	echo "0x2000c?W 0x2f737100" | adb -w /etc/init
 *
 *  Note that one should not forget the root password, otherwise
 *  miniunix should be loaded from tape; the rootfs should be mounted
 *  in /a after booting disk(,,1)vmunix -as etc.
 *
 *  Note also that this solution works for SUN Release 3.2, 3.4, and 3.5.
 *  It probably works for other 3.x versions.  It almost certainly DOESN'T
 *  work for 4.0, but it isn't needed for 4.0, either (you can just remove
 *  the "secure" attribute from the console line in /etc/ttytab).
 *
 *  The user gets two tries to enter a valid password; if he fails,
 *  the program halts the machine.
 *
 *  This code originally received from Carol Wilwelmy (carolw@sun.com)
 *  of Sun Microsystems, and subsequently adapted by D. Brent Chapman
 *  (brent@capmkt.com) of Capital Market Technology, Inc.  Both of us
 *  will be pleased if you find our work useful, but you're playing with
 *  the very guts of your system; if you screw it up, the "solution" may be
 *  worse than the problem it solves...
 *
 *  NO WARRANTIES; USE AT YOUR OWN RISK!!!  If you screw up your 'init',
 *  you'll have to boot your system diskless from tape or a server in order
 *  to restore the old version of init (which you were supposed to save
 *  before you started playing around).  
 */


#include <pwd.h>
#include <signal.h>
#include <sgtty.h>
#include <sys/file.h>

extern	struct passwd *getpwnam();
extern	char	*crypt(), *getpass();
extern	int (*signal())();
extern	int alarm();

char	*sorry	= "\rSorry\n\r";
char	*failed = "\r/bin/sq: getpwnam and getpwuid for user root failed\n\r";
char	*halt   = "/etc/halt";
char	*halting= "\rHalting machine\n\r";

void
punt(status)
    int status;

{
    write(2, halting, strlen(halting));
    execl(halt, halt, 0);
    /* shouldn't get here, but just in case... */
    exit(status);
}


main(argc, argv)
    int argc;
    char *argv[];
{
    char *password;
    struct passwd *pwd;
    int i; 

    if (argc == 1 && strcmp(argv[0], "-") == 0) {
	if (access("/single_shot", F_OK) == 0)
	    goto ok;
	if ((pwd = getpwnam("root")) == (struct passwd *)0) {
	    /* apparently no user called "root"; try for uid==0 */
	    if ((pwd = getpwuid(0)) == (struct passwd *)0) {
	        write(2, failed, strlen(failed));
	        punt(1);
	    }
	}
	if (pwd->pw_passwd[0] == '\0')
	    goto ok;
	for (i = 0; i < 2; i++) {
	    password = getpass("Password:");
	    if (strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0) {
		if (i == 0) {
		    write(2, sorry, strlen(sorry));
		    continue;
		}
		write(2, sorry, strlen(sorry));
		punt(2);
	    }
	    else
		break;
	}
    }
ok:
    execv("/bin/sh", argv);
    /* shouldn't get here */
    punt(3);
}

miker@central.sun.com (Mike Raffety) (05/06/89)

Brent Chapman ({cogsci,lll-tis,uunet}!capmkt!brent) says:
>  You must do the mv & cp before beginning because the current /etc/init
>  file is "busy" (because the program is running) and can't be modified.

/etc/init is compiled to NOT page (i.e., pure executable), so that you CAN
delete/update it without crashing your system (when it goes to page out of
a non-existent or changed file).

I like the binary patch to /etc/init, but instead of the custom "sq"
program, a simpler solution might be to make a hard (or soft) link called
"sq" (or any other two letter name, to match the space used in /etc/init
for "sh") to /bin/login, which will do just as effective a job of keeping
the machine safe.

Also, if you screw up your /etc/init so that it's unusable, you can use an
undocumented flag to boot, "-i", to specify an alternate version of
/etc/init to use (e.g., /etc/init.orig, like "> b sd(0,0,0)vmunix -i
/etc/init.orig").  Of course, anyone else can, too, so once you've
verified your modification works, delete the unsecure version of
/etc/init.

brent@uunet.uu.net (Brent Chapman) (05/06/89)

Mike Raffety <uunet!Central.Sun.COM!sunloop!oconnor!porsche!miker>
writes:
# I like the binary patch to /etc/init, but instead of the custom "sq"
# program, a simpler solution might be to make a hard (or soft) link
# called "sq" (or any other two letter name, to match the space used in
# /etc/init for "sh") to /bin/login, which will do just as effective a
# job of keeping the machine safe.

I'm not sure this is a good idea.  Doesn't /bin/login try to write a
record into /etc/utmp, and so on?  If you're booting single-user, then the
root partition hasn't been fsck'd yet, and if it's hosed, you _don't_ want
to go writing on it before you fsck it.

One thing about 'sq' is that it's fairly simple, and you can pretty much
just stare at it for a while and convince yourself that it does what it's
supposed to, and doesn't do anything unexpected (like write the the root
partition before it's been fsck'd); login, by comparison, is big and
complicated, and there are lots of ways it might go wrong or do something
unexpected in this situation.


-Brent
--
Brent Chapman					Capital Market Technology, Inc.
Computer Operations Manager			1995 University Ave., Suite 390
brent@capmkt.com				Berkeley, CA  94704
{cogsci,lll-tis,uunet}!capmkt!brent		Phone:  415/540-6400

miker@central.sun.com (Mike Raffety) (05/07/89)

> I'm not sure this is a good idea.  Doesn't /bin/login try to write a
> record into /etc/utmp, and so on?  If you're booting single-user, then
> the root partition hasn't been fsck'd yet, and if it's hosed, you
> _don't_ want to go writing on it before you fsck it.

Well, /etc/rc.boot is already doing a "> /etc/mtab" to create an empty
file in your root, and mount (since root is mounted r/w on 3.x) is marking
the filesystem as mounted.  A write to /etc/utmp seems fairly harmless.
Indeed, one might consider it a feature (;-) to have a record of
single-user boots.

rbj@dsys.icst.nbs.gov (Root Boy Jim) (05/11/89)

Brent Chapman <capmkt!brent@uunet.uu.net>:

? Read the comments very carefully and think hard before proceeding; this
? "fix" involves a binary patch to /etc/init, and if you make a mistake,
? recovery could be painful.

True enuf, but why not just edit /etc/init with emacs? True, not everyone
*has* emacs, but you could at least mention the possibility for those who
do.

	Root Boy Jim is what I am
	Are you what you are or what?