HSC
Network Security Consulting Agency Since 1989 - Specialized in Unix, Windows, TCP/IP and Internet
Text mode: access to the page content
Hervé Schauer Consultants
You are here: Home > Resources > Tips > chrooter un compte avec OpenSSH
Go to: HSC Trainings
Télécharger le catalogue des formations
Search:  
Version française
   Services   
o Skills & Expertise
o Consulting
o ISO 27001 services
o Audit & Assessment
o Penetration tests
o Vunerability assessment (TSAR)
o Forensics
o ARJEL
o Training courses
o E-learning
   Conferences   
o Agenda
o Past events
o Tutorials
   Resources   
o Thematic index
o Tips
o Lectures
o Courses
o Articles
o Tools (download)
o Vulnerability watch
   Company   
o Hervé Schauer
o Team
o Job opportunities
o Credentials
o History
o Partnerships
o Associations
   Press and
 communication
 
 
o HSC Newsletter
o Bulletin juridique HSC
o Press review
o Press releases
o Publications
   Contacts   
o How to reach us
o Specific inquiries
o Directions to our office
o Hotels near our office
|>|chrooter un compte avec OpenSSH  

by Denis Ducamp (23/04/2001)



Cet article montre un test d'utilisation de chroot (mise en cage) des accès
avec OpenSSH en version 2.5.2p2 . Ceci peut être utile pour ne donner à un
utilisateur l'accès qu'à un environnement restreint ou plus restrictivement
de ne lui permettre que la publication de données.

OpenSSH est une version libre (sources et utilisation) des version 1.5 et
2.0 du protocole SSH. Cette mise en oeuvre est disponible sur
http://www.openssh.com/ .

Le patch ci-dessous, testé sous Linux et NetBSD (par Stéphane Aubert), est
une version mise à jour du patch de Ricardo Cerqueira disponible dans le
répertoire contrib de la distribution portable de OpenSSH.

Le principe est extrêmement simple : il faut changer le répertoire principal
de l'utilisateur de telle sorte que le démon sshd comprenne qu'il lui faut
chrooter l'utilisateur. Pour ceci, l'entrée suivante dans le fichier
/etc/passwd :

	ducamp:x:1000:1000:Denis Ducamp,,,:/home/ducamp:/bin/bash

peut être modifiée comme ceci :

	ducamp:x:1000:1000:Denis Ducamp,,,:/home/./ducamp:/bin/bash

ou comme cela :

	ducamp:x:1000:1000:Denis Ducamp,,,:/home/ducamp/./:/bin/bash

Dans le premier cas (/home/./ducamp) le serveur sshd comprend que
l'utilisateur doit être chrooté dans le répertoire /home et que le
répertoire principal de l'utilisateur, relativement au sommet de la cage,
est /ducamp .

Dans le second cas (/home/ducamp/./) le serveur sshd comprend que
l'utilisateur doit être chrooté dans le répertoire /home/ducamp et que le
répertoire principal de l'utilisateur, relativement au sommet de la cage,
est / .

Il faut ensuite peupler la cage avec quelques exécutables indispensables...

Dans le cas de l'accès à un shell, il faut recopier dans la cage le shell et
les bibliothèques nécessaires à celui-ci :

# ldd /home/ducamp/bin/bash 
        libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001c000)
        libdl.so.2 => /lib/libdl.so.2 (0x40020000)
        libc.so.6 => /lib/libc.so.6 (0x40023000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
# mkdir -p $CAGE/bin $CAGE/lib
# cp /bin/bash $CAGE/bin
# cp /lib/libtermcap.so.2 /lib/libdl.so.2 /lib/libc.so.6 /lib/ld-linux.so.2 $CAGE/bin

Attention, sur certains systèmes (Linux libc5, *BSD), le programme de
chargement des bibliothèques n'est pas montré par ldd. Pour savoir où il est
localisé, il suffit d'utiliser la commande suivante :

	# strings ...bin/bash | egrep /ld.elf.so

ce qui peut donner par exemples les résultats suivants :

	/usr/libexec/ld.elf_so
ou	/usr/libexec/ld-elf.so.1

Pour tester, il est possible de lancer un serveur sshd sur un autre port :

	# ./sshd -p 2222

ou pour avoir un maximum d'informations de débogage sous les systèmes Linux :

	# strace -f ./sshd -d -p 2222 > strace.txt 2>&1

et sous les systèmes BSD :

	# ktrace -i sshd -p 2222 -d
	# kdump

puis de s'y connecter :

	$ ssh localhost -x -p 2222

Exemple de session dans le cas /home/./ducamp , l'utilisateur étant chrooté
dans /home :

bash-2.04$ pwd
/ducamp
bash-2.04$ echo /bin/* /lib/*
/bin/bash /lib/ld-linux.so.2 /lib/libc.so.6 /lib/libcrypt.so.1
/lib/libdl.so.2 /lib/libnsl.so.1 /lib/libtermcap.so.2 /lib/libutil.so.1
bash-2.04$ echo $$
17776

Pour vérifier, il est possible de visualiser dans une autre session les
caractéristiques du shell ainsi obtenu. Sous Linux il est possible
d'utiliser ainsi /proc :

# ls -l /proc/17776/{cwd,exe,root}
lrwx------   1 ducamp   ducamp          0 Apr 20 17:09 /proc/17776/cwd -> /home/ducamp
lrwx------   1 ducamp   ducamp          0 Apr 20 17:09 /proc/17776/exe -> /home/bin/bash
lrwx------   1 ducamp   ducamp          0 Apr 20 17:09 /proc/17776/root -> /home

Le shell exécuté est correctement chrooté dans /home , ce shell est
/home/bin/bash (/bin/bash par rapport à la racine de la cage qui est ici
/home) et son répertoire actuel est /home/ducamp (/ducamp par rapport à
la racine de la cage).

De façon générale, lsof peut être utilisé ainsi :

# lsof -p 17776
COMMAND   PID   USER   FD   TYPE DEVICE    SIZE   NODE NAME
bash    17776 ducamp  cwd    DIR    3,9    4096  32163 /home/ducamp
bash    17776 ducamp  rtd    DIR    3,9    4096      2 /home
bash    17776 ducamp  txt    REG    3,9  477756 212928 /home/bin/bash
bash    17776 ducamp  mem    REG    3,9  424087 212932 /home/lib/ld-linux.so.2
bash    17776 ducamp  mem    REG    3,9   14831 212929 /home/lib/libtermcap.so.2
bash    17776 ducamp  mem    REG    3,9   62615 212930 /home/lib/libdl.so.2
bash    17776 ducamp  mem    REG    3,9 4746015 212931 /home/lib/libc.so.6
bash    17776 ducamp    0u   CHR 136,30             32 /dev/pts/30
bash    17776 ducamp    1u   CHR 136,30             32 /dev/pts/30
bash    17776 ducamp    2u   CHR 136,30             32 /dev/pts/30
bash    17776 ducamp  255u   CHR 136,30             32 /dev/pts/30

Ici aussi il est possible de voir les mêmes données sur les lignes où FD est
égal à cwd (répertoire courant), rtd (répertoire racine) et txt (programme
exécuté).

Dans le cas de l'accès au serveur via sftp (protocole dédié au transfert de
fichiers via ssh), il faut recopier dans la cage le module sftp-server et
les bibliothèques nécessaires à celui-ci :

# ldd /usr/local/libexec/sftp-server
        libz.so.1 => /usr/lib/libz.so.1 (0x4001c000)
        libnsl.so.1 => /lib/libnsl.so.1 (0x4002b000)
        libutil.so.1 => /lib/libutil.so.1 (0x40040000)
        libcrypto.so.0 => /usr/lib/libcrypto.so.0 (0x40044000)
        libcrypt.so.1 => /lib/libcrypt.so.1 (0x40104000)
        libc.so.6 => /lib/libc.so.6 (0x40131000)
        libdl.so.2 => /lib/libdl.so.2 (0x4023f000)
        /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
# mkdir -p $CAGE/usr/local/libexec $CAGE/usr/lib
# cp /usr/local/libexec/sftp-server $CAGE/usr/local/libexec
# cp /lib/libnsl.so.1 /lib/libutil.so.1 /lib/libcrypt.so.1 $CAGE/lib
# cp /usr/lib/libz.so.1 /usr/lib/libcrypto.so.0 $CAGE/usr/lib

Un serveur sshd pourra être lancé sur un autre port, puis la commande
suivante lancée pour vérifier le bon fonctionnement :

	$ sftp -o 'port 2222' localhost

Voici un exemple de session :

sftp> pwd
Remote working directory: /ducamp
sftp> dir /lib
drwxr-xr-x    2 root     root         4096 Apr 20 14:06 .
drwxr-xr-x   12 root     root         4096 Apr 20 15:05 ..
-rwxr-xr-x    1 root     root        14831 Aug 21  1999 libtermcap.so.2
-rwxr-xr-x    1 root     root        62615 Feb 19 06:57 libdl.so.2
-rwxr-xr-x    1 root     root      4746015 Feb 19 06:58 libc.so.6
-rwxr-xr-x    1 root     root       424087 Feb 19 06:58 ld-linux.so.2
-rwxr-xr-x    1 root     root        71527 Feb 19 06:57 libcrypt.so.1
-rwxr-xr-x    1 root     root        42626 Feb 19 06:58 libutil.so.1
-rwxr-xr-x    1 root     root       350956 Feb 19 06:58 libnsl.so.1

Et voici une vérification rapide des propriétés du module sftp-server
exécuté :

$ ps ax|grep [s]ftp-server
18995 ?        S      0:00 /usr/local/libexec/sftp-server
$ ls -l /proc/18995/{cwd,exe,root}
lrwx------   1 ducamp   ducamp          0 Apr 20 18:25 /proc/18995/cwd -> /home/ducamp
lrwx------   1 ducamp   ducamp          0 Apr 20 18:25 /proc/18995/exe -> /home/usr/local/libexec/sftp-server
lrwx------   1 ducamp   ducamp          0 Apr 20 18:25 /proc/18995/root -> /home

Puis en utilisant lsof :

# lsof -p 18995
COMMAND     PID   USER   FD   TYPE     DEVICE    SIZE   NODE NAME
sftp-serv 18995 ducamp  cwd    DIR        3,9    4096  32163 /home/ducamp
sftp-serv 18995 ducamp  rtd    DIR        3,9    4096      2 /home
sftp-serv 18995 ducamp  txt    REG        3,9   23752 213116 /home/usr/local/libexec/sftp-server
sftp-serv 18995 ducamp  mem    REG        3,9  424087 212932 /home/lib/ld-linux.so.2
sftp-serv 18995 ducamp  mem    REG        3,9   62776  61294 /home/usr/lib/libz.so.1
sftp-serv 18995 ducamp  mem    REG        3,9  350956 213163 /home/lib/libnsl.so.1
sftp-serv 18995 ducamp  mem    REG        3,9   42626 213121 /home/lib/libutil.so.1
sftp-serv 18995 ducamp  mem    REG        3,9  796880  61295 /home/usr/lib/libcrypto.so.0
sftp-serv 18995 ducamp  mem    REG        3,9   71527 213119 /home/lib/libcrypt.so.1
sftp-serv 18995 ducamp  mem    REG        3,9 4746015 212931 /home/lib/libc.so.6
sftp-serv 18995 ducamp  mem    REG        3,9   62615 212930 /home/lib/libdl.so.2
sftp-serv 18995 ducamp    0u  unix 0xcbbf4000          46713 socket
sftp-serv 18995 ducamp    1u  unix 0xcbbf4000          46713 socket
sftp-serv 18995 ducamp    2u  unix 0xc9c9fc40          46715 socket
sftp-serv 18995 ducamp    3u  unix 0xcbbf4000          46713 socket
sftp-serv 18995 ducamp    4u  unix 0xcbbf4000          46713 socket

Remarque : même pour sftp, il est nécessaire que le shell de l'utilisateur
soit présent dans la cage et qu'il puisse être exécuté. En effet, les
commandes passées par l'utilisateur sont exécutées par l'option -c du shell
de l'utilisateur.

Afin de rajouter le support de scp et rsync, il est nécessaire de recopier
les exécutables scp et rsync dans la cage ainsi que les bibliothèques dont
ils ont besoin. La remarque pour sftp s'applique également ici : le shell de
l'utilisateur doit être présent et exécutable.

Maintenant pour restreindre le compte à certaines commandes, il est
nécessaire de générer un couple de clés et de limiter l'usage d'une clé
publique à un commande précise.

La génération d'un couple de clés est faisable par les commandes :
	$ ssh-keygen -t rsa
ou	$ ssh-keygen -t dsa
générant respectivement des clés rsa et dsa pour SSHv2.

La clé publique du client (id_rsa.pub ou id_dsa.pub) doit être copiée dans
le fichier ~/.ssh/authorized_keys2 côté serveur et il suffit d'insérer
l'option suivante au début de la ligne :

	command="/usr/local/libexec/sftp-server"

Ici le serveur ssh exécutera systématiquement la commande
/usr/local/libexec/sftp-server quelque soit la commande demandée par le
client. Si le client se connecte grâce à sftp alors tout marche bien puisque
dans ce cas la commande sftp-server est lancée par le serveur sshd. Si le
client se connecte simplement en ssh, la commande sftp-server sera lancée
côté serveur et le client ne peut pas se retrouver avec un shell.

Pour rsync la situation est plus complexe car le "serveur" rsync lancé par
le client prend des options qui dépendent des options utilisées par le
client. Ainsi il est nécessaire de décider d'une ligne de commande rsync qui
sera exécutée et de se tenir à celle-ci. Par exemple pour synchroniser un
serveur web personnel, la commande utilisateur est :

	rsync -avr --delete --rsh=ssh public_html/ perso@web:public_html/

et la commande à mettre dans le fichier .ssh/authorized_keys2 est :

	command="/usr/bin/rsync --server -vlogDtpr --delete . public_html/"

Dans certains cas où la clé privée ne peut être chiffrée, car elle devra
être utilisée par des scripts automatiques, il est possible de limiter sur
le serveur le droit d'utilisation de la clé par adresses IP. Pour cela, il
faut insérer l'option suivante au début de la ligne de la clé publique :

	from="liste d'adresses"

Cette liste d'adresses, séparées par des virgules, peut être composée
d'adresses IP et de noms entièrement qualifiés. Les caractères génériques
'*' et '?' peuvent être utilisés.

Il vous est maintenant possible de créer un compte ssh chrooté. Ce compte
peut être utilisé, par exemple, pour ne permettre d'accéder qu'à un petit
nombre de commandes qui auront été recopiées dans la cage. Ce compte peut
également être beaucoup plus restrictif en n'acceptant qu'une commande
déterminée, comme sftp-server ou rsync, ce qui peut permettre par exemple de
publier des données.

Faites attention toutefois à ce que dans certains cas l'utilisateur ne
puisse pas modifier certains fichiers tels que les exécutables et les
fichiers de configuration (authorized_keys par exemple). Afin de faire cela
complètement, OpenSSH a aujourd'hui besoin de quelques patches en plus qui
seront publiés dans un autre tips...

------------ chroot.diff ------------
diff -u openssh-2.5.2p2.orig/session.c openssh-2.5.2p2/session.c
--- openssh-2.5.2p2.orig/session.c	Thu Mar 22 01:58:27 2001
+++ openssh-2.5.2p2/session.c	Mon Apr 23 15:39:56 2001
@@ -93,6 +93,8 @@
 # include <uinfo.h>
 #endif
 
+#define CHROOT
+
 /* types */
 
 #define TTYSZ 64
@@ -1012,6 +1014,10 @@
 	extern char **environ;
 	struct stat st;
 	char *argv[10];
+#ifdef CHROOT
+	char *user_dir;
+	char *new_root;
+#endif /* CHROOT */
 	int do_xauth = s->auth_proto != NULL && s->auth_data != NULL;
 #ifdef WITH_IRIX_PROJECT
 	prid_t projid;
@@ -1064,6 +1070,26 @@
 			set_limits_from_userattr(pw->pw_name);
 # endif /* HAVE_GETUSERATTR */
 # ifdef HAVE_LOGIN_CAP
+
+#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 (setusercontext(lc, pw, pw->pw_uid,
 			    (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
 				perror("unable to set user context");
@@ -1085,6 +1111,26 @@
 
 			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);
------------ chroot.diff ------------



Last modified on 12 November 2003 at 13:55:00 CET - webmaster@hsc.fr
Mentions légales - Information on this server - © 1989-2013 Hervé Schauer Consultants