I needed to create a secure environment on solaris where the requirements called for no user having access to anything in the filesystem, or anything from any other user - in fact, not even appearing on the same filesystem. Obviously, a chrooted environment would work best, but OpenSSH's sshd did not support that. Looking all around the 'net, I was unable to find specific instructions for having OpenSSH support chroot for Solaris. Therefore, I found what information I could from other Linux HOWTO's, changed it a bit, and set it up for myself.
These instructions are a bit generalized, and are probably already out of date. OpenSSH, OpenSSL, and GNU gcc probably already have updated versions of their code and/or binaries, but I believe this is a good starting point for anyone else facing the same (or similar) requirements.
First and foremost, we need to ensure that we have at the very least some important versions of the files in question: OpenSSH 3.4p1, OpenSSL 0.9.6g[1], etc.
I have written this HOWTO from a standpoint that, because I'm going to have to do it again, I'd like to have as much of it automated as possible. Therefore, I have every step listed with not only what it does, but in a convenient cut-and-paste format so I could paste the commands directly into my nearest terminal emulator and save my fingers some work. I would normally put everything into one mammoth script, but I like to make absolutely certain I don't miss any error messages. Therefore, I have the steps separated logically into sections that are related.
Disclaimer: Please carefully read all instructions fully before proceeding. If you don't understand what these instructions do, you may run into problems. Be advised,when you follow these instructions, you do so at your own risk. I cannot and will not be held liable for any damages to your system.
Several versions of OpenSSH's sshd between 2.3.1 and 3.3 contain an input validation error that can result in an integer overflow and privilege escalation. All versions between 2.3.1 and 3.3 contain a bug in the PAMAuthenticationViaKbdInt code. All versions between 2.9.9 and 3.3 contain a bug in the ChallengeResponseAuthentication code. OpenSSH 3.4 and later are not affected[2]. Therefore, you need to make sure you have the latest version of OpenSSH as well as the latest versions of the supporting libraries, and especially the compiler. Uninstall older versions of any of the following packages:
gcc
zlib
libgcc
openssl
prngd
i.e.:
pkgrm `pkginfo | grep gcc | awk '{print $2}'`
pkgrm `pkginfo | grep zlib | awk '{print $2}'`
pkgrm `pkginfo | grep libgcc | awk '{print $2}'`
pkgrm `pkginfo | grep ssl | awk '{print $2}'`
pkgrm `pkginfo | grep ssh | awk '{print $2}'`
pkgrm `pkginfo | grep prngd | awk '{print $2}'`
Get and install the following packages (available from sunfreeware.com and mirrors):
gcc-3.2-sol8-sparc-local (ftp://ftp.sunfreeware.com/pub/freeware/sparc/8/gcc-3.2-sol8-sparc-local.gz) zlib-1.1.4-sol8-sparc-local (ftp://ftp.sunfreeware.com/pub/freeware/sparc/8/zlib-1.1.4-sol8-sparc-local) libgcc-3.2-sol8-sparc-local (ftp://ftp.sunfreeware.com/pub/freeware/sparc/8/libgcc-3.2-sol8-sparc-local) openssl-0.9.6g-sol8-sparc-local (ftp://ftp.sunfreeware.com/pub/freeware/sparc/8/openssl-0.9.6g-sol8-sparc-local) zlib-1.1.4-sol8-sparc-local (ftp://ftp.sunfreeware.com/pub/freeware/sparc/8/zlib-1.1.4-sol8-sparc-local) prngd-0.9.23-sol8-sparc-local (ftp://ftp.sunfreeware.com/pub/freeware/sparc/8/prngd-0.9.23-sol8-sparc-local) i.e.: ( while true;do echo all; echo y; done) | /usr/sbin/pkgadd -d \ gcc-3.2-sol8-sparc-local > /dev/null 2>&1; break ( while true;do echo all; echo y; done) | /usr/sbin/pkgadd -d \ zlib-1.1.4-sol8-sparc-local > /dev/null 2>&1; break ( while true;do echo all; echo y; done) | /usr/sbin/pkgadd -d \ libgcc-3.2-sol8-sparc-local > /dev/null 2>&1; break ( while true;do echo all; echo y; done) | /usr/sbin/pkgadd -d \ openssl-0.9.6g-sol8-sparc-local > /dev/null 2>&1; break ( while true;do echo all; echo y; done) | /usr/sbin/pkgadd -d \ zlib-1.1.4-sol8-sparc-local > /dev/null 2>&1; break ( while true;do echo all; echo y; done) | /usr/sbin/pkgadd -d \ prngd-0.9.23-sol8-sparc-local > /dev/null 2>&1; break
You will need a way to generate random (or at least pseudo-random) numbers. I have chosen to use prngd, and the rest of these instructions assume that[3]. Therefore, setup and run prngd:
echo "setting up PRNGD startup script..."
echo "#!/bin/sh" > /etc/init.d/prngd
echo "pid=\`/usr/bin/ps -e | /usr/bin/grep prngd | /usr/bin/sed \
-e 's/^ *//' -e 's/ .*//'`" >> /etc/init.d/prngd
echo "case \$1 in" >> /etc/init.d/prngd
echo "'start')" >> /etc/init.d/prngd
echo " /usr/local/bin/prngd /var/spool/prngd/pool;;" >> /etc/init.d/prngd
echo "'stop')" >> /etc/init.d/prngd
echo " if [ \"\${pid}\" != \"\" ]; then" >> /etc/init.d/prngd
echo " /usr/bin/kill \${pid}; fi;;" >> /etc/init.d/prngd
echo "*)" >> /etc/init.d/prngd
echo " echo \"usage: /etc/init.d/prngd (start|stop)\";;" >> \
/etc/init.d/prngd
echo "esac" >> /etc/init.d/prngd
echo "adjusting permissions..."
chown root:sys /etc/init.d/prngd
chmod 555 /etc/init.d/prngd
ln -s /etc/init.d/prngd /etc/rc2.d/S98prngd
ln -s /etc/init.d/prngd /etc/rc0.d/K02prngd
echo "generating entropy..."
cat /var/log/syslog /var/adm/messages > /usr/local/etc/prngd/prngd-seed
mkdir /var/spool/prngd
/etc/rc2.d/S98prngd start
Get gunzip, and untar the source code tarball (available from http://www.openssh.com/portable.html ) :
openssh-3.4p1.tar.gz (ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-3.4p1.tar.gz)
Apply the following patch (copy & paste to file: openssh-chroot-3.4p1.patch or click here )[4]:
--CUT-HERE--
*** ./openssh-3.4p1/session.c Wed Jun 26 06:51:06 2002
--- ./openssh-3.4p1-chroot/session.c Mon Jul 8 10:35:44 2002
***************
*** 64,69 ****
--- 64,71 ----
#define is_winnt (GetVersion() < 0x80000000)
#endif
+ #define CHROOT
+
/* func */
Session *session_new(void);
***************
*** 1159,1164 ****
--- 1161,1171 ----
void
do_setusercontext(struct passwd *pw)
{
+ #ifdef CHROOT
+ char *user_dir;
+ char *new_root;
+ #endif /* CHROOT */
+
char tty='\0';
#ifdef HAVE_CYGWIN
***************
*** 1187,1192 ****
--- 1194,1220 ----
if (setlogin(pw->pw_name) < 0)
error("setlogin failed: %s", strerror(errno));
+
+ #ifdef CHROOT
+ user_dir = xstrdup(pw->pw_dir);
+ new_root = user_dir + 1;
+
+ while((new_root = strchr(new_root, '.')) != NULL) {
+ new_root--;
+ if(strncmp(new_root, "/./", 3) == 0) {
+ *new_root = '\0';
+ new_root += 2;
+
+ if(chroot(user_dir) != 0)
+ fatal("Couldn't chroot to user directory %s", user_dir);
+
+ pw->pw_dir = new_root;
+ break;
+ }
+ new_root += 2;
+ }
+ #endif /* CHROOT */
+
if (setgid(pw->pw_gid) < 0) {
perror("setgid");
exit(1);
--CUT-HERE--
Ensure your PATH is correct for compiling (you'll need to have at the very least gcc, ar, and as in your PATH):
export PATH=$PATH:/usr/local/bin:/usr/ccs/bin
Run configure for openssh[5].
Using prngd:
./configure --with-prngd-socket=/var/spool/prngd \ --prefix=/usr/local --libexecdir=/usr/libexec/openssh \ --sysconfdir=/usr/local/etc --mandir=/usr/share/man
Using /dev/random:
./configure --with-random=/dev/random \ --prefix=/usr/local --libexecdir=/usr/libexec/openssh \ --sysconfdir=/usr/local/etc --mandir=/usr/share/man
Build openssh[6]:
make
Ensure you have everything setup correctly for privilege separation:
echo "creating directory..."
if [[ ! -d /var/empty ]]
then
/usr/bin/mkdir /var/empty
/usr/bin/chown root:sys /var/empty
/usr/bin/chmod 755 /var/empty
fi
echo "creating ssh user/group..."
if [[ `grep sshd /etc/group` == "" ]]
then
/usr/sbin/groupadd sshd
fi
if [[ `grep sshd /etc/passwd` == "" ]]
then
/usr/sbin/useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd
fi
Install the compiled openssh:
make install
Generate keys (if you haven't already done so with a previous version of openssh):
echo "generating keys..." if [[ ! -e /usr/local/etc/ssh_host_key ]]; then /usr/local/bin/ssh-keygen -t rsa1 \ -f /usr/local/etc/ssh_host_key -N ""; fi if [[ ! -e /usr/local/etc/ssh_host_dsa_key ]]; then /usr/local/bin/ssh-keygen \ -t dsa -f /usr/local/etc/ssh_host_dsa_key -N ""; fi if [[ ! -e /usr/local/etc/ssh_host_rsa_key ]]; then /usr/local/bin/ssh-keygen \ -t rsa -f /usr/local/etc/ssh_host_rsa_key -N ""; fi
Setup startup script for sshd and use it to start sshd:
echo "setting up SSH startup script..."
echo "#!/bin/sh" > /etc/init.d/sshd
echo "pid=\`/usr/bin/ps -e | /usr/bin/grep sshd | \
/usr/bin/sed -e 's/^ *//' -e 's/ .*//'\`" >> /etc/init.d/sshd
echo "case \$1 in" >> /etc/init.d/sshd
echo "'start')" >> /etc/init.d/sshd
echo " /usr/local/sbin/sshd;;" >> /etc/init.d/sshd
echo "'stop')" >> /etc/init.d/sshd
echo " if [ \"\${pid}\" != \"\" ]; then" >> /etc/init.d/sshd
echo " /usr/bin/kill \${pid}; fi;;" >> /etc/init.d/sshd
echo "*)" >> /etc/init.d/sshd
echo " echo \"usage: /etc/init.d/sshd (start|stop)\";;" >> /etc/init.d/sshd
echo "esac" >> /etc/init.d/sshd
echo "adjusting permissions..."
chown root:sys /etc/init.d/sshd
chmod 555 /etc/init.d/sshd
ln -s /etc/init.d/sshd /etc/rc2.d/S98sshd
ln -s /etc/init.d/sshd /etc/rc0.d/K02sshd
echo "starting sshd..."
/etc/rc2.d/S98sshd startYou now need to define your jail directory, for example (download a handy ksh script here):
JAILUSER=jailusername JAILGROUP=jailusers mkdir /export/home/jail /usr/sbin/groupadd $JAILGROUP chown root:$JAILGROUP /export/home/jail chmod 750 /export/home/jail
I take the extra step per my specific requirements and create a new jail for every separate user who needs it, but you can alternatively just have proper permissions inside your single jail[7].
/usr/sbin/useradd -g $JAILGROUP -c "Jail user $JAILUSER" \ -d /export/home/jail/$JAILUSER/./home/$JAILUSER -s /bin/sh $JAILUSER mkdir /export/home/jail/$JAILUSER chown $JAILUSER:$JAILGROUP /export/home/jail/$JAILUSER
You need the following directory structure inside your jail[8].
cd /export/home/jail/$JAILUSER mkdir etc mkdir bin mkdir usr mkdir usr/bin mkdir usr/local mkdir usr/local/bin mkdir usr/local/libexec mkdir usr/local/sbin mkdir usr/local/lib mkdir usr/local/ssl mkdir usr/local/ssl/lib mkdir usr/lib mkdir usr/platform mkdir usr/platform/`uname -i` mkdir usr/platform/`uname -i`/lib mkdir dev mkdir devices mkdir devices/pseudo mkdir home
Copy most of the various binaries and libraries into your jail[9]:
cd /export/home/jail/$JAILUSER
APPS='bin/cp bin/ls bin/mkdir bin/mv bin/pwd bin/rm bin/rmdir bin/sh'
for i in $APPS; do
cp /$i ./$i
LIBS=`ldd ./$i | awk '{print $3}'`
for l in $LIBS; do
mkdir ./`dirname $l` > /dev/null
cp $l .$l
done
doneCreate pseudo devices and links for dev/null and dev/zero[10]:
cd /export/home/jail/$JAILUSER/devices/pseudo mknod mm@0:zero c 13 12 mknod mm@0:null c 13 2 cd /export/home/jail/$JAILUSER/dev ln -s ../devices/psuedo/mm@0:zero zero ln -s ../devices/pseudo/mm@0:null null
Copy the other binaries and libraries you'll be needing (ldd on solaris doesn't quite pickup all of these)[11] (be sure to scroll all the way to the right of this page; the <PRE> HTML tags don't let this scroll):
cd /export/home/jail/$JAILUSER BINS="usr/local/bin/ssh usr/local/libexec/sftp-server usr/local/sbin/sshd usr/local/lib/libz.so usr/local/ssl/lib/libcrypto.so.0.9.6 usr/lib/ld.so.1 usr/platform/`uname -i`/lib/libc_psr.so.1 usr/lib/nss_files.so.1" for i in $BINS; do cp /$i ./$i done
Create the users' home directory inside the jail:
mkdir /export/home/jail/$JAILUSER/home/$JAILUSER chown $JAILUSER:$JAILGROUP /export/home/jail/$JAILUSER/home/$JAILUSER
Create empty passwd and group files inside your jail[12]:
touch /export/home/jail/$JAILUSER/etc/passwd touch /export/home/jail/$JAILUSER/etc/group
Test your jail by typing (as root)[13]:
chroot /export/home/jail/jailuser /bin/sh
Test your jail by attempting sftp and/or ssh from another host:
ssh jailuser@jailhost
Resources:
Notes:
I have noticed that OpenSSL is (as of the time of this writing) at version 0.9.6g. These instructions are correct for version 0.9.6e. I will update this when I confirm that everything still works for the newer OpenSSL codebase.
The preceding text is entirely lifted from http://www.openssh.com/txt/preauth.adv.
Alternatively (and perhaps more recommended), install the /dev/random solaris patch (112438-01), reboot, and continue with step 4. In situations where rebooting is not possible, there are some instructions that may work for you that do not require a reboot: http://www.sunmanagers.org/pipermail/summaries/2002-April/002956.html.
Thanks to Eileen Coles for reminding me about the corresponding kill scripts.
Some variation of
patch < openssh-chroot-3.4p1.patchshould work, but my patch complained about the file format. I attributed the error to perhaps not using a GNU version of patch when I got the patch file from a linux HOWTO. I saw that there wasn't much code to patch, so I typed it in manually.
On my test system (relatively new Sun Netra X1), this took long enough for me to walk across the street for a cup of coffee. YMMV.
I ran into a large number of problems here when I originally attempted to compile the code. Parts of the compatibility code (and perhaps more, I couldn't get past that) will not compile with up to gcc 3.0.2, the compiler I originally had on this system. The most fatal errors were 'unknown opcode .previous' and 'unknown opcode .subsection'. Upgrading to gcc 3.2 took care of these issues, but still left a number of (apparently harmless) 'warning: changing search order for system directory "/usr/local/include"' and 'inet_aton.c:155: warning: subscript has type 'char''. By the time I upgraded my compiler, I was content to let these warnings go.
The rest of these instructions are specific to my situation with a separate jail for each user. These steps are easily modified to accommodate a single jail.
Perhaps obviously, the output of the `uname -i` directory will vary depending on your architecture.
Thanks to Alex Kramarov and Gabriele Facciolo for the majority of this script.
Thanks to james@firstaid.com for pointing out that these device files were necessary, though I made these a bit more solaris-ish in linking to ../devices/pseudo/etc.
Again, inspired in large part by Alex Kramarov and Gabriele Facciolo.
My installation performed acceptably with empty password and group files. If you run into problems, try:
echo "$JAILUSER:x:`/usr/xpg4/bin/id -u $JAILUSER`:`/usr/xpg4/bin/id -g $JAILGROUP`::/home/$JAILUSER:/bin/sh" > \ /export/home/jail/$JAILUSER/etc/passwdand
echo "$JAILGROUP::`/usr/xpg4/bin/id -g $JAILGROUP`:$JAILUSER" > \ /export/home/jail/$JAILUSER/etc/group
I was very fortunate in that when this failed the first few times I tried it, I got errors telling me which library wasn't found. However, my very first few tests consisted of attempting to ssh as the chrooted user, and having it fail without giving any indication of any error, at least none that I was able to find.
Epilogue:
I'm always open to constructive criticism about how to make these instructions more clear, more current, or even more correct. All examples are believed to run correctly under the Korn shell.
Questions, comments, clarifications, corrections, and additions may be addressed to kent@c2group.net. I attempt to properly credit all sources of information for anything referenced in this document.