print · rss · source

Patch Linux : bind() sur un port privilégié sans être root

Ce patch se base sur un patch kernel qui associe à chaque port une ACL et sur une application qui permet de mettre à jour ces ACL via un nouvel appel système.

Le patch

Les sources

Installation

La mise en oeuvre du patch nécessite la recompilation du noyau.
Pour appliquer le patch aux sources :

$ tar xfz linux-2.x.y.tgz
$ cd linux-2.x.y
$ patch -p1 < ../patch-portacl-2.x.y

La commande port_acl_set

Les sources

Compilation

$ gcc -Wall -o port_acl_set port_acl_set.c

Utilisation

La commande port_acl_set prend 3 paramètres :

usage : port_acl_set <+->port user
  • + et - pour ajouter ou supprimer une autorisation d'accès
  • le numéro de port
  • le login de l'utilisateur

Par exemple, pour autoriser l'utilisateur franck (uid 632) à acceder au port 80 :

# ./port_acl_set +80 franck

Pour visualiser les utilisateurs ayant accès aux ports privilégiés, il faut lire le fichier /proc/net/port_acl. On remarque que les login des utilisateurs ne sont pas donnés, seulement leur uid :

# cat /proc/net/port_acl
80: 632

Si on souhaite enlever à l'utilisateur ayant l'uid 632 (ici franck) le droit d'acceder au port 80 :

# ./port_acl_set -80 franck

Implémentation

L'implémentation repose sur un principe assez simple. Le fichier source net/ipv4/af_inet.c contient l'implémentation de l'appel système inet_bind() qui fait appel à la fonction de contrôle capable() pour empêcher les utilisateurs non privilégiés d'accèder à un port privilégié (<1024) :

snum = ntohs(addr->sin_port);
err = -EACCES;
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
    goto out;

L'implémentation de la fonction capable() est réalisée dans plusieurs fichiers. Pour avoir un aperçu, cliquer sur capable().

kernel/capability.c :

int __capable(struct task_struct *t, int cap)
{
        if (security_capable(t, cap) == 0) {
                t->flags |= PF_SUPERPRIV;
                return 1;
        }
        return 0;
}
EXPORT_SYMBOL(__capable);

int capable(int cap)
{
        return __capable(current, cap);
}
EXPORT_SYMBOL(capable);

include/linux/security.h :

#ifdef CONFIG_SECURITY
static inline int security_capable(struct task_struct *tsk, int cap)
{
        return security_ops->capable(tsk, cap);
}
#else /* CONFIG_SECURITY */
static inline int security_capable(struct task_struct *tsk, int cap)
{
        return cap_capable(tsk, cap);
}
#endif  /* CONFIG_SECURITY */

security/commoncap.c :

int cap_capable (struct task_struct *tsk, int cap)
{
        /* Derived from include/linux/sched.h:capable. */
        if (cap_raised(tsk->cap_effective, cap))
                return 0;
        return -EPERM;
}

include/linux/capability.h :

#define cap_raised(c, flag)  (cap_t(c) & CAP_TO_MASK(flag))

include/linux/init_task.h :

#define INIT_TASK(tsk)  \
{                                                                       \
[...]
        .cap_effective  = CAP_INIT_EFF_SET,                             \
        .cap_inheritable = CAP_INIT_INH_SET,                            \
        .cap_permitted  = CAP_FULL_SET,                                 \

arch/i386/kernel/init_task.c :

/*
 * Initial task structure.
 *
 * All other task structs will be allocated on slabs in fork.c
 */
struct task_struct init_task = INIT_TASK(init_task);

Le patch ajoute la fonction port_acl() qui retourne 1 si le user courant a les droits d'accès au port passé en argument :

if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE) && !port_acl(snum))

L'essentiel du patch implémente la mise à jour des ACL sous forme de liste chaînée.


print · rss · source
Page last modified on December 11, 2008, at 11:54 AM