I wrote chrsh one evening because I wanted a "shell" that I could assign to a user that would jail that user using Unix's chroot() and then execute a real shell within the jail environment. For instance, I wanted to give a shell account to a friend that would only be used for running the tintin++ MUD client, but I did not want the friend to be able to see the entire system. So I created a small "mini" system in a subdirectory and use chrsh as his login shell. Now when he logs in, he is effectively jailed to that mini environment.

I am aware that should a jailed process obtain root privileges within the jail, that chroot() currently cannot prevent that process from breaking out. I expect great care and thought to go into any jail envirnment that will use chrsh, as much or more than one would normally take for an entire system with shell users.

So far chrsh is rather FreeBSD specific, only compiled and tested by myself on FreeBSD. I don't have access to any other platforms at this time to check how well it works/doesn't work there. If someone else does port it, e-mail me the heavily commented patches and I'll consider including the changes in future versions.

Thanks, Paul for reporting that you've got it working on Solaris 2.6.

And thanks to H. D. Moore for adding syslog() logging capabilities and for the username buffer bug fix.

WARNING:

If you use chrsh to allow users shell access and use SSH, please be aware that some versions of the SSH server can permit the remote user to bypass their local shell setting ("ssh -l username -t hostname /bin/sh") and still get access to a shell that is NOT chrooted. This problem does NOT affect all users of SSH. Additionally, keep in mind that SSH may also permit the user to use IP forwarding, enabling the user to act as if he/she were connecting FROM the server where SSH resides, or even operate IP services that get forwarded to the user's computer.

Well here it is. The latest version is 1.1:

Source:

chrsh-1.1.c (Version 1.1)
chrsh-1.0.c (Version 1.0)
chrsh-0.6.0.c (Version 0.6.0)

Example Usage:

The chrsh wrapper program is very picky about how the jail is set up, who owns what, and the permissions of things. If after you set it up, it doesn't seem to work, check the log file (/var/log/chrsh.log by default unless you change it in the chrsh.c source file) to see what the wrapper did not like.

The chrsh program needs to be set up in /etc/master.passwd as the user's shell. The user's home directory needs to use a format similar to wuftp, using the /./ to indicate where the jail begins. Look at the chrsh.c file's configuration section before compiling to set up who will own what and what permissions the wrapper should expect. The wrapper expects the GECOS information to be identical within the chroot jail as it appears in the real /etc/master.passwd database.

Now for the example:

An example set-up on a FreeBSD workstation using chrsh to "jail"
or confine the user to a small controlled portion of the system.

From /etc/master.passwd (password omitted)

  testuser:*:2000:2000::0:0:Test User:/usr/home/testuser/./home:/bin/chrsh

From /etc/group

  testgroup:*:2000:

The following directories were then created:

  /usr/home/testuser       -- owned by root.wheel, mode 0555
  /usr/home/testuser/bin   -- owned by root.wheel, mode 0555
  /usr/home/testuser/etc   -- owned by root.wheel, mode 0555
  /usr/home/testuser/home  -- owned by testuser.testgrp, mode 0750

The following files were then copied:

  /bin/sh to /usr/home/testuser/bin/sh -- owned by root.wheel, mode 0555

A small selection of /bin/* utilities was also copied to /usr/home/testuser/bin

Next, this file was compiled thus:

  cc -o chrsh chrsh.c
  cp chrsh /bin/chrsh
  chown root.wheel /bin/chrsh
  chmod 04555 /bin/chrsh

Finally, the /usr/home/testuser/etc directory was set up thus:

  /usr/home/testuser/etc/shells -- owned by root.wheel, mode 0555
  -- contained only 1 line:

      /bin/sh


  /usr/home/testuser/etc/group -- owned by root.wheel, mode 0555
  -- contained a minimal set of groups:

      wheel:*:0:
      testuser:*:2000:
      nogroup:*:65533:
      nobody:*:65534:


  /usr/home/testuser/etc/master.passwd -- owned by root.wheel, mode 0600
  -- contained a minimal set of users:

      root:*:0:0::0:0:Root System Administrator:/root:/bin/sh
      testuser:*:2000:2000::0:0:Test User:/home:/bin/sh

To set up the FreeBSD password database within the jail:

  cd /usr/home/testuser/etc
  pwd_mkdb -d . master.passwd

This set up the pwd.db and spwd.db files in /usr/home/testuser/etc

Other systems will use different files/methods for creating a minimal
user and group database in the jailed /usr/home/testuser/etc directory.

That was basically all there was to it:

# telnet localhost
Trying 127.0.0.1...
Connected to localhost.infowest.com.
Escape character is '^]'.



Welcome to my FreeBSD box.

login: testuser
Password:
Last login: Fri Jul 10 04:35:34 from localhost
Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
	The Regents of the University of California.  All rights reserved.

FreeBSD 2.2.6-STABLE (LOCAL) #0: Sun Jul  5 08:45:33 MDT 1998
$ ls -lFa
total 4
drwxr-x---  2 testuser  testuser  512 Jul 10 10:32 ./
dr-xr-xr-x  5 root      wheel     512 Jul 10 10:32 ../
$ pwd
/home

Misc. Musings:

Here's some of my rough thoughs and ideas that spawned this excursion
into wrapper coding:

	If the user's shell is "chrsh" then look at the user's
	home directory and parse it.  It should follow wu_ftpd
	standards for specifying chroot() point and home subdir.

	Example:
		joe:*:200:200::Joe User:/chroot/jail/./home/joe:/bin/chrsh

	This shell MUST be suid root so it can perform the chroot()
	function to the jail directory.  Also, the jail directory
	MUST be owned by the UID and GID specified in the config.
	section.  Additionally, the home subdir directory must be
	owned by the user in question with group ownership the same
	GID as in /etc/passwd.  It must NOT be world writable.
	Finally, a minimal "/bin" and "/etc" must exist in the chroot
	jail, owned by specified UID and GID, using the specified
	mode.

	For the jail to be secure, there MUST be a password database
	in the jail that is NOT world writable in a secure place,
	and there should be no suid/sgid programs in the jail.  They
	could be used to compromise the jail.

	Once the chroot() takes place, we check the pw stuff in the
	chrooted jail and it MUST match EXACTLY EXCEPT for the home
	dir and shell.  The home dir MUST match the home subdir
	portion of the real pw entry, and the shell MUST be a
	safely owned executable and MUST have an entry in the
	chrooted /etc/shells file.

	After all checks are done, this prog. gives up root privs.
	and becomes the user in question.  The environment vars. HOME
	and SHELL are set to the chroot jail versions, and the
	new shell is execve()'d.  Just before the execve(), the log
	file is flagged by a fcntl() call for automatic closing should
	the execve() succeed.  I don't know if this call is cross-
	platform, or FreeBSD specific.

	If the execve() fails, the log file descriptor is still valid
	permitting the program to add some final log entries to
	describe the problem.


If root or some other potentially useful UID or GID is compromised
within the chroot environment, all bets are off.  Hence I recommend
against ALL suid and sgid programs within the environment.  Period.

Change Log:

Date:
Version:
Changes:
7 Sept. 2000 1.0 beta 1 Merged H D Moore's SYSLOG functionality and username buffer bug fix. Now you can use either syslog() for logging, or chrsh's direct-file-append logging. The default is to use syslog().
11 Feb. 1999 0.6.0 Added support for passing arguments on to the shell. This permits non-interactive uses (like "ssh -l user example.com 'ls -l | ./choke.pl'")

That's all the documentation ya get, folks!  :)

Aaron out.