Trouver les capacités (capabilities) d’un programme

Suite à l’article précédent sur les capacités POSIX, il restait une question en suspens. Pouvoir trouver les capacités nécéssaire à un programme; pour cela, on peut utiliser la capacité qu’offre le noyau pour « court circuiter » des fonctions: Kernel Probes

Le principe est simple si on connaît la fonction appellée, on crée un module ayant une fonction similaire (même prototype) qui log les demandes de capacités selon un nom de programme passé en paramètre au module. Ce dernier nécessite que le noyau ait certaines options d’activées (CONFIG_KPROBES, CONFIG_KALLSYMS_ALL, CONFIG_KALLSYMS entre autres) ce qui n’est pas le cas par défaut sur Arch Linux par exemple, vous pouvez télécharger une version modifiée du PKGBUILD du paquet kernel26 version 2.6.32.7-1: kernel26-2.6.32.7-1.src.tar.gz

Le module est celui de l’article de Serge E. Hallyn [1], j’y ai juste rajouté le paramètre pour le nom du programme à surveiller ainsi qu’un tableau pour me sortir directement la capacité en texte.
Archive: capable_probe.tar.gz
capable_probe.c:

// Original taken from http://www.ibm.com/developerworks/library/l-posixcap.html
// By Serge E. Hallyn (sergeh at us.ibm.com)
//
// Modified by tuxce 
//

#include 
#include 
#include 
#include 



static char progname[255];
module_param_string(progname, progname, sizeof(progname), 0644);
MODULE_PARM_DESC(progname, "Program name.");

static const char *probed_func = "cap_capable";

static char *cr_cap[] = { "CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", 
	"CAP_FOWNER", "CAP_FSETID", "CAP_KILL",
	"CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", 
	"CAP_LINUX_IMMUTABLE", "CAP_NET_BIND_SERVICE", 
	"CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW", 
	"CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", 
	"CAP_SYS_RAWIO", "CAP_SYS_CHROOT", "CAP_SYS_PTRACE", 
	"CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", 
	"CAP_SYS_NICE", "CAP_SYS_RESOURCE", "CAP_SYS_TIME", 
	"CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE", 
	"CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", 
	"CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN" };

int cr_capable (struct task_struct *tsk, const struct cred *cred, int cap, 
	int audit)
{
	if(strcmp(tsk->comm,progname) == 0)
		if (cap >=0 && cap <= 33)
		{
			printk(KERN_NOTICE "%s: asking for capability %s for %s\n",
				__FUNCTION__, cr_cap[cap], tsk->comm);
		}
		else
		{
			printk(KERN_NOTICE "%s: asking for capability %d for %s\n",
				__FUNCTION__, cap, tsk->comm);
		}
	jprobe_return();
	return 0;
}

static struct jprobe jp = {
	.entry = JPROBE_ENTRY(cr_capable)
};

static int __init kprobe_init(void)
{
	int ret;
	jp.kp.symbol_name = (char *)probed_func;

	if ((ret = register_jprobe(&jp)) < 0) {
		printk("%s: register_jprobe failed, returned %d\n",
			__FUNCTION__, ret);
		return -1;
	}
	return 0;
}

static void __exit kprobe_exit(void)
{
	unregister_jprobe(&jp);
	printk("capable kprobes unregistered\n");
}

module_init(kprobe_init);
module_exit(kprobe_exit);

MODULE_LICENSE("GPL");

Makefile:

# Taken from http://www.ibm.com/developerworks/library/l-posixcap.html
obj-m := capable_probe.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
install: default
	install -d /lib/modules/$(shell uname -r)/kernel/misc/
	install capable_probe.ko /lib/modules/$(shell uname -r)/kernel/misc/
	depmod -a
clean:
	rm -f *.mod.c *.ko *.o

On compile:

$ make
make -C /lib/modules/2.6.32-ARCH/build SUBDIRS=/home/tuxce/posix/capable modules
make[1]: entrant dans le répertoire « /usr/src/linux-2.6.32-ARCH »
  CC [M]  /home/tuxce/posix/capable/capable_probe.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      /home/tuxce/posix/capable/capable_probe.mod.o
  LD [M]  /home/tuxce/posix/capable/capable_probe.ko
make[1]: quittant le répertoire « /usr/src/linux-2.6.32-ARCH »
$ sudo insmod capable_probe.ko progname=ping

Si tout se passe bien, vous devrez pouvoir récuperer les capacités demandées dans le log, /var/log/messages.log pour Arch Linux:

$ ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.064 ms

--- 127.0.0.1 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.064/0.064/0.064/0.000 ms
$ tail -2 /var/log/messages.log
Feb  8 17:30:44 host kernel: cr_capable: asking for capability CAP_NET_RAW for ping
Feb  8 17:30:44 host kernel: cr_capable: asking for capability CAP_SETUID for ping

Dans le log, on voit que ping demande CAP_NET_RAW (qu'on a utilisé dans l'exemple de l'article précédent), de même que CAP_SETUID, cette dernière est utilisée pour passer de root à l'utilisateur exécutant la commande, mais comme on a supprimé le setuid, ce n'est plus nécessaire.

L'utilisation du module est assez simple, ou bien on le décharge puis charge avec le bon paramètre, ou on modifie le paramètre en cours de fonctionnement:

sudo cp /usr/bin/crontab{,.x}
echo -n crontab.x | sudo tee /sys/module/capable_probe/parameters/progname
crontab.x -e

Et voyons voir ce que demande crontab:

Feb  8 18:18:33 host kernel: cr_capable: asking for capability CAP_SETGID for crontab.x
Feb  8 18:18:33 host kernel: cr_capable: asking for capability CAP_DAC_OVERRIDE for crontab.x
Feb  8 18:18:33 host kernel: cr_capable: asking for capability CAP_DAC_OVERRIDE for crontab.x

Là, on a exécuté la commande "crontab -e" car elle demande plus de privilèges que par exemple "crontab -l".
Selon la sortie du log, pour se débarasser du setuid de crontab:

sudo setcap CAP_DAC_OVERRIDE,CAP_SETGID=ep crontab


Ressources:
POSIX file capabilities: Parceling the power of root

Commentaire (1)