|
|
 | |  |  | Installation sécurisée d'un serveur POP3 |  |
by Renaud Deraison (30/11/2000)
Renaud Deraison est une personne que l'on ne présente plus, Nessus est
aujourd'hui connu et reconnu partout. Renaud a souhaité participer aux
brèves HSC, il est le bien-venu, nous apprécions la qualité de ce qu'il
écrit. Merci Renaud d'autres suivront peut-être ton exemple.
Chrooter cucipop et le faire tourner en tant que non-root sous FreeBSD 3.1
--------------------------------------------------------------------------
Par Renaud Deraison <deraison@nessus.org>
Ce document explique comment j'ai fait pour chrooter cucipop et le faire tourner
en tant qu'utilisateur n'ayant aucun privilège sous FreeBSD 3.1 (si ce n'est
le privilège de lire et écrire les mails de tout le monde)
. -2 Pourquoi ?
---------------
Parceque les daemon pop sont les meilleurs moyens d'administrer une machine
à distance après ssh. Régulièrement on y trouve des bugs, et ceux-ci font
toujours très mal. Alors autant réduire l'exposabilité d'une machine et
faire tourner cucipop avec le moins de privilèges possible.
De plus, la plupart des utilisateurs utilisent pop sans chiffrement. On ne veut
pas que leur mot de passe pop soit le meme que le mot de passe système, donc
on veut avoir deux bases de mots de passes différentes.
Enfin, je sais qu'il existe des serveurs POP qui font déjà tout ca. J'avais
pas envie de les essayer, etant très content avec cucipop et d'un naturel
paresseux.
. -1 Ingrédients
----------------
- Les sources de cucipop 1.31, que nous patcherons
- Les sources de la libc de FreeBSD, que nous patcherons
- GCC
- Ce document
- Un peu de temps et de patience. Eventuellement une
machine pour assurer le service pop pendant l'install
- Les droits de root :) Toutes les commandes effectuées ici
doivent etre faites en tant que root.
0. Création d'un utilisateur
----------------------------
Mon daemon cucipop tourne en tant que "cucipop". Chez moi, ils sont de uid/gid
5002 (le numéro est important, on s'en servira tout à l'heure et c'est utilisé
dans le patch inclus dans ce document).
1. Création de la prison
------------------------
Je stocke toutes mes prisons dans /usr/local/chroots/ :
mkdir /usr/local/chroots/cucipop
cd /usr/local/chroots/cucipop
mkdir bin
mkdir dev
mkdir etc
mkdir tmp
chown cucipop tmp
chmod 0755 tmp
mkdir usr
mkdir var
Dans /bin, on mettra le binaire cucipop que nous allons créer plus tard.
Dans /dev, on veut créer le fichier spécial "null" :
2. Création des devices (null et log)
-------------------------------------
cd /usr/local/chroots/cucipop/dev
mknod c 2 2 null
chmod 0666 null
On édite /etc/rc.conf pour que notre serveur syslogd ouvre une socket
dans /usr/local/chroots/cucipop/dev/log :
dans /etc/rc.conf, je rajoute/modifie la ligne syslogd_flags :
syslogd_flags="-l /usr/local/chroots/cucipop/dev/log"
3. Remplissage de /etc
----------------------
J'ai besoin de remplir le /etc de ma prison. Je prend les fichiers suivants
dans le vrai /etc :
group
master.passwd
passwd
popusers
pwd.db
shells
spwd.db
(on notera que là c'est pas une bonne chose parcequ'on reprend les mots
de passe du systeme. Je ferais une autre breve qui explique comment modifier
la commande 'passwd' sous FreeBSD pour gérer plusieurs bases de mots de passe).
Je rend ces fichiers lisibles à tous (ce qui défait l'interet de /etc/spwd.db,
je le concède. On pourrait ruser en rendant ces fichiers lisibles
par le groupe cucipop seulement, mais ne rentrons pas trop dans les détails) :
chgrp cucipop /usr/local/chroots/cucipop/etc/*
chmod 0640 /usr/local/chroots/cucipop/etc/*
Dans ce meme etc, je créé un dossier "mail", qui appartient à l'utilisateur et
au group cucipop :
mkdir mail
chown cucipop.cucipop mail
chmod 0755 mail/
cucipop y creera son vpop.db par la suite.
4. Remplissage de /usr
----------------------
Je recopie les fichiers suivants dans /usr/local/chroots/cucipop/usr
(je suis en FreeBSD 3.1, ca peut changer selon votre système - faites
un ldd sur l'executable de votre cucipop pour savoir ce dont vous avez
besoin).
mkdir /usr/local/chroots/cucipop/usr/lib
cp /usr/lib/libcrypt.so.2 /usr/local/chroots/cucipop/usr/lib
cp /usr/lib/libmd.so.2 /usr/local/chroots/cucipop/usr/lib
cp /usr/lib/libskey.so.2 /usr/local/chroots/cucipop/usr/lib
cp /usr/lib/libutil.so.2 /usr/local/chroots/cucipop/usr/lib
(on notera que je ne recopie PAS la libc, parceque par défaut elle
ne laisse pas un utilisateur non-root accéder à /etc/spwd.db)
Je continue :
mkdir /usr/local/chroots/cucipop/usr/libexec
cp /usr/libexec/ld-elf.so.1 /usr/local/chroots/cucipop/usr/libexec
5. Remplissage de /var
----------------------
Autrefois, votre mail s'entassait dans /var/mail. On le change de là mais
on met un symlink pour que les autres programmes du système continuent
de fonctionner. On utilise tar pour préserver les permissions :
(on arrete notre MTA avant - ie: postfix stop)
tar -cpf - /var/mail | tar -xpf - -C /usr/local/chroots/cucipop
rm -rf /var/mail
ln -s /usr/local/chroots/cucipop/mail /var/mail
(on redemarre notre MTA)
Cucipop va avoir besoin d'écrire dans ce dossier (pour le locking) :
chown cucipop.cuciop /usr/local/chroots/cucipop/var/mail
chmod 0775 /usr/local/chroots/cucipop/var/mail
On créé de plus les dossiers dont cucipop aura besoin :
mkdir /usr/local/chroots/cucipop/var/spool
mkdir /usr/local/chroots/cucipop/var/spool/cucipop
chown cucipop.cucipop /usr/local/chroots/cucipop/var/spool/cucipop
chmod 0755 /usr/local/chroots/cucipop/var/spool/cucipop
mkdir /usr/local/chroots/cucipop/var/run
chown cucipop.cucipop /usr/local/chroots/cucipop/var/run
chmod 0755 /usr/local/chroots/cucipop/var/run
6. Modification de la libc
--------------------------
La libc de FreeBSD utilise /etc/spwd.db si et seulement si notre uid est nul.
Il faut donc changer ce comportement pour que notre daemon cucipop puisse
utiliser les mots de passes du système.
Dans /usr/src/sys/lib/libc/gen/getpwent.c, je modifie la fonction
__initdb, pour changer :
if(!geteuid()) p = _PATH_SMP_DB;
en :
if((!geteuid())||(geteuid() == 5002))p = _PATH_SMP_DB;
(on notera bien que 5002 c'est l'uid de mon utilisateur cucipop).
Je recompile et j'installe cette libc dans /usr/local/chroots/cucipop/usr/lib
(et PAS dans /usr/lib). Une fois cette manipulation faite, je restaure
le fichier getpwent.c dans son état original pour éviter des erreurs
à l'avenir.
7. Modification de cucipop
--------------------------
Je télécharge cucipop 1.31 sur ftp.cuci.nl, dans /pub, et j'y applique
les patches suivants (un mélange de patches maison et de patches venant du port
FreeBSD) :
----- COUPER ICI -----
diff -urN cucipop-1.31/Makefile cucipop-1.31.patched/Makefile
--- cucipop-1.31/Makefile Thu Nov 30 02:02:21 2000
+++ cucipop-1.31.patched/Makefile Sat Sep 30 14:18:45 2000
@@ -1,30 +1,30 @@
#$Id: cucipop.tip,v 1.1 2000/11/30 09:44:28 aubert Exp $
-BASENAME= /usr
+BASENAME= $(PREFIX)
-GCC_WARNINGS = -O2 -pedantic -Wreturn-type -Wunused -Wformat \
- -Wpointer-arith -Wconversion -Waggregate-return \
+#CC_WARNINGS = -O2 -pedantic -Wreturn-type -Wunused -Wformat \
+# -Wpointer-arith -Wconversion -Waggregate-return \
#-Wimplicit -Wshadow #-Wuninitialized
#
# Omit USE_DB if you don't have the -ldb2 library (Berkeley DB, v2.x)
# WARNING: bulletins are not remembered to have been deleted without USE_DB
-CFLAGS = -O #-DUSE_DB #$(GCC_WARNINGS)
-LDFLAGS = -lcrypt #-ldb2
+#CFLAGS = -O -DUSE_DB #$(GCC_WARNINGS)
+LDFLAGS += -lcrypt -lmd
# If you change this, edit config.h as well
-CUCIPOPLIB=/var/lib/cucipop
+CUCIPOPLIB=/var/spool/cucipop
CUCIPOPBULLETINS=$(CUCIPOPLIB)/bulletins
O=o
-BINDIR=$(BASENAME)/sbin
+BINDIR=$(BASENAME)/libexec
MANDIR=$(BASENAME)/man/man8
-INSTALL=install -o root -m
-BINPERM=02755 -s -g mail
-REGPERM=0644
+INSTALL=install -c -o bin -m
+BINPERM=02555 -s -g mail
+REGPERM=0444
#
# When compiling without APOP support, the md5 library can be omitted.
@@ -32,11 +32,11 @@
MD5_OBJ=md5/md5c.$(O)
OBJS=cucipop.$(O) authenticate.$(O) atotime.$(O) locking.$(O) xcreat.$(O) \
- dbops.$(O) hsort.$(O) simplecrypt.$(O) $(MD5_OBJ)
+ dbops.$(O) hsort.$(O) simplecrypt.$(O)
-BINS=cucipop makevpopdb
+BINS=cucipop# makevpopdb
-MANS=cucipop.8 makevpopdb.8
+MANS=cucipop.8
all: $(BINS)
@@ -70,8 +70,8 @@
install: $(BINS) $(MANS)
$(INSTALL) $(BINPERM) $(BINS) $(BINDIR)
$(INSTALL) $(REGPERM) $(MANS) $(MANDIR)
- mkdir $(CUCIPOPLIB) 2>/dev/null; exit 0
- mkdir $(CUCIPOPBULLETINS) 2>/dev/null; exit 0
+ mkdir -p $(CUCIPOPLIB) 2>/dev/null; exit 0
+ mkdir -p $(CUCIPOPBULLETINS) 2>/dev/null; exit 0
@for a in $(BINS); do ls -l $(BINDIR)/$$a; done
@for a in $(MANS); do ls -l $(MANDIR)/$$a; done
diff -urN cucipop-1.31/authenticate.c cucipop-1.31.patched/authenticate.c
--- cucipop-1.31/authenticate.c Wed May 13 18:57:39 1998
+++ cucipop-1.31.patched/authenticate.c Sat Sep 30 14:18:45 2000
@@ -44,7 +44,7 @@
#define VIRTUALUSER "vpop"
#ifndef MAILSPOOLDIR
-#define MAILSPOOLDIR "/var/spool/mail/" /* watch the trailing / */
+#define MAILSPOOLDIR "/var/mail/" /* watch the trailing / */
#endif
#ifndef MAILSPOOLHASH
#define MAILSPOOLHASH 0 /* 2 would deliver to /var/spool/mail/b/a/bar */
diff -urN cucipop-1.31/config.h cucipop-1.31.patched/config.h
--- cucipop-1.31/config.h Tue May 12 23:09:14 1998
+++ cucipop-1.31.patched/config.h Sat Sep 30 14:18:45 2000
@@ -3,7 +3,7 @@
#define USEdot_lock /**/
/*#define USEfcntl_lock /**/ /* to test which combinations make sense */
/*#define USElockf /**/ /* run the lockingtest program part of */
-/*#define USEflock /**/ /* the procmail installation process */
+#define USEflock /**/ /* the procmail installation process */
/*#define SHADOW_PASSWD /**/ /* shadow password library support */
@@ -84,6 +84,6 @@
#define MAXBULLETINS 64
#define MAXSTATEAGE 8388608 /* > 3 months */
#define MEMORY_CACHE (64*1024)
-#define CUCIPOP_LIB "/var/lib/cucipop"
+#define CUCIPOP_LIB "/var/spool/cucipop"
#define STATE_DB "state.db"
#define BULLETINS_PATH CUCIPOP_LIB"/bulletins"
diff -urN cucipop-1.31/cucipop.8 cucipop-1.31.patched/cucipop.8
--- cucipop-1.31/cucipop.8 Mon May 11 18:35:19 1998
+++ cucipop-1.31.patched/cucipop.8 Sat Sep 30 14:18:47 2000
@@ -48,12 +48,12 @@
.SH NAME
cucipop \- Cubic Circle POP3 daemon
.SH SYNOPSIS
-.B /usr/sbin/cucipop
+.B !!PREFIX!!/libexec/cucipop
.RB [ \-qaYdPUSDAT ]
.RB [ "\-E \fIage\fP" ]
.RB [ "\-p \fIport\fP" ]
.br
-.B /usr/sbin/cucipop
+.B !!PREFIX!!/libexec/cucipop
.B \-v
.ad
.SH DESCRIPTION
@@ -132,12 +132,12 @@
.IR port .
.SH EXAMPLES
Typically
-.I pop-3
+.I pop3
service is defined in
.BR services (5)
as follows:
.Sx 1
-pop-3 110/tcp
+pop3 110/tcp
.Ex
In order to start cucipop from within
.BR inetd (8),
@@ -145,16 +145,16 @@
.BR inetd.conf (5)
would be suitable:
.Sx 1
-pop-3 stream tcp nowait root /usr/sbin/cucipop cucipop -Y
+pop3 stream tcp nowait root /usr/sbin/cucipop cucipop -Y
.Ex
If your site gets many hits from popclients, it would be preferable
to start cucipop standalone as in:
.Sx 1
-/usr/sbin/cucipop -Y
+!!PREFIX!!/libexec/cucipop -Y
.Ex
Your typical BOFH setting would be:
.Sx 1
-/usr/sbin/cucipop -YaSE 6w
+!!PREFIX!!/libexec/cucipop -YaSE 6w
.Ex
.SH FILES
.TP 2.3i
@@ -166,25 +166,25 @@
.BR makevpopdb (8)
man page on how this file is created
.TP
-.B /var/lib/cucipop/state.db
+.B !!PREFIX!!/lib/cucipop/state.db
AI state information and bulletin history
.TP
-.B "/var/lib/cucipop/bulletins/\fInn\fP"
+.B "!!PREFIX!!/lib/cucipop/bulletins/\fInn\fP"
.B 00
through
.B 63
optional bulletin files in regular mailbox format
.TP
-.B /var/spool/mail/$LOGNAME
+.B /var/mail/$LOGNAME
system mailbox
.TP
-.B /var/spool/mail/virtual.dom.ain/$LOGNAME
+.B /var/mail/virtual.dom.ain/$LOGNAME
virtual host system mailbox
.TP
-.B /var/spool/mail/$LOGNAME.lock
+.B /var/mail/$LOGNAME.lock
lockfile for the system mailbox
.TP
-.B /var/spool/mail/virtual.dom.ain/$LOGNAME.lock
+.B /var/mail/virtual.dom.ain/$LOGNAME.lock
lockfile for the virtual host system mailbox
.TP
.B _????`hostname`
diff -urN cucipop-1.31/cucipop.c cucipop-1.31.patched/cucipop.c
--- cucipop-1.31/cucipop.c Wed May 13 18:57:39 1998
+++ cucipop-1.31.patched/cucipop.c Sat Sep 30 15:07:12 2000
@@ -238,6 +238,7 @@
static const auth_identity*transmogrify(const auth_identity*pass)
{ initappdb();opendb();
+ return pass;
return setuid(auth_whatuid(pass))?(auth_identity*)0:pass;
}
@@ -520,6 +521,7 @@
if(!(fmbox=fp=fopen(mailbox=auth_mailboxname((auth_identity*)pass),"r+b")))
{ if(errno==ENOENT)
goto nomailbox; /* no mailbox found */
+ fprintf(sockout, "-ERR cuci: no mailbox %s (%s)\n", mailbox, strerror(errno));
return 0;
}
ungetc(fgetc(fp),fp); /* prime atime */
@@ -725,6 +727,12 @@
return EX_OSFILE;
}
fclose(stderr);
+ initgroups("cucipop", 5002);
+ setuid(5002);
+ seteuid(5002);
+ setgid(5002);
+ setegid(5002);
+
if(fork()>0)
return EXIT_SUCCESS;
setsid();listen(serverfd,LISTEN_BACKLOG);
diff -urN cucipop-1.31/locking.h cucipop-1.31.patched/locking.h
--- cucipop-1.31/locking.h Wed Dec 18 12:52:06 1996
+++ cucipop-1.31.patched/locking.h Sat Sep 30 14:54:05 2000
@@ -16,3 +16,12 @@
#ifndef USEfcntl_lock
#define NOfcntl_lock
#endif
+
+
+#undef USEfcntl_lock
+#undef USElockf
+#undef USEflock
+#undef USEdot_lock
+
+#define USEflock
+
diff -urN cucipop-1.31/xcreat.c cucipop-1.31.patched/xcreat.c
--- cucipop-1.31/xcreat.c Sat May 2 02:56:54 1998
+++ cucipop-1.31.patched/xcreat.c Sat Sep 30 14:18:45 2000
@@ -22,7 +22,7 @@
/*#define NOuname /* uncomment if uname is not available */
/*#define NOstrpbrk /* uncomment if strpbrk is not available */
/*#define strchr(s,c) index(s,c) /* uncomment if strchr is not available */
-#define const /* can be undefined for ANSI compilers */
+/*#define const /* can be undefined for ANSI compilers */
#include <unistd.h> /* open() close() link() unlink()
getpid() */
----- FIN DES PATCHS ----
Je compile mon binaire (make), et je l'installe dans
/usr/local/chroots/cucipop/bin
8. Rendre les mails lisibles à cucipop
--------------------------------------
Bon avec tout ca, on est presque prets, mais le problème c'est que
cucipop ne peut pas lire les mails qui sont dans
/usr/local/chroots/cucipop/var/mail/, puisque ceux-ci sont en mode
0600.
L'astuce consiste donc à faire appartenir tout le contenu de
/var/mail au groupe cucipop. Le problème c'est que souvent, certains
MTA vont remettre les mails en mode 0600. On a donc besoin de mettre
en place un cronjob qui va restaurer nos permissions toutes les minutes :
mkdir /var/scripts
cat << _EOF_ > /var/scripts/cucipop
#!/bin/sh
# on remet le contenu de /etc en lecture au groupe :
cd /usr/local/chroots/cucipop/etc/ && chmod g+r *
# on remet les mails en mode 0660
cd /usr/local/chroots/cucipop/var/mail/ && chgrp cucipop * && chmod 0660 *
_EOF_
chmod 0755 /var/scripts/cucipop
Il ne nous reste plus qu'a mettre en place le cronjob. En tant que root, je
fait :
crontab -e
Je rajoute :
*/5 * * * * /var/scripts/cucipop
Je sauve et je quitte.
9. Script de démmarrage
-----------------------
Il ne me reste plus qu'a écrire un script de démarrage :
cd /usr/local/etc/rc.d
cat << _EOF_ > cucipop.sh
#!/bin/sh
/usr/sbin/chroot /usr/local/chroots/cucipop /bin/cucipop -Ya && echo -n ' cucipop'
_EOF_
10. C'est fini (?)
------------------
Voila, il ne me reste plus qu'a lancer cucipop (en tant que root) en faisant
/usr/local/etc/rc.d/cucipop.sh
Le patch qu'on lui a appliqué le force a faire un set[e][gu]id(5002), ce qui
le fait tourner en tant que 'cucipop' :
ps aux|grep cucipop
cucipop 28239 0.0 0.2 820 116 ?? Is 30Sep00 0:36.46 /bin/cucipop
cucipop 90567 0.0 0.0 0 0 ?? Z - 0:00.00 (cucipop)
Je recommande d'installer sslwrap pour chiffrer la communication client <->
serveur, mais ca risque d'etre off-topic.
11. Conclusion
--------------
Faire tourner un serveur pop en tant que non-root implique un certain compromis
au niveau de la sécurité locale (les mails passent en mode 0660), mais
permet de mieux dormir la nuit. Une intrusion par ce biais restera hélas
une expérience pénible, mais aux conséquences relativement limitées.
Bientot :
- cucipop et sslwrap en 10mn
- comment faire tourner CVS chrooté
- comment faire tourner dhcpcd chrooté
- comment faire tourner squid chrooté
- comment faire tourner ftpd chrooté
- comment gérer les bases de mots de passe de tout ce petit monde
|