Hi,

We would like to change the AppArmor profile management we have in https://github.com/Parrot-Developers/firmwared and switch to a whitelist strategy, however we encounter some issues.

The attached C program (based on https://github.com/Parrot-Developers/firmwared/blob/master/src/folders/instances.c) shows one of those issues. This program can be compiled using the attached Makefile by simply typing "make".

# Usage :

Suppose you have compiled busybox statically :
git://busybox.net/busybox.git
make menuconfig
LDFLAGS="--static" make

And you put it in the /tmp/aa-chroot/ folder like so :
/tmp/aa-chroot/bin/busybox
/tmp/aa-chroot/sbin/busybox

Now load the provided aa-chroot profile using the following command :
/sbin/apparmor_parser --replace < aa-chroot.profile

Then, as root, run the aa-chroot program :
./aa-chroot /tmp/aa-chroot /sbin/busybox sh

# Issue

What I don't understand is that the profile seems to have a default allow policy although I thought deny was the default policy in AppArmor. Indeed, the /bin/busybox sh call gets correctly denied because of the explicit "audit deny /bin/* lrwxk" rule, however the "/sbin/busybox sh" call is successful.

Could you explain to me why the default policy is allow instead of deny and how can I change this ?

If that can be of any use, please note that I am on Ubuntu Xenial.

Any help would be greatly appreciated.

Thanks in advance,

Pierre Zurek

#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE
#endif /* _XOPEN_SOURCE */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif /* _GNU_SOURCE */
#include <linux/limits.h>
#include <sched.h>
#include <errno.h>
#include <sys/mount.h>
#include <stdlib.h>
#include <argz.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdbool.h>
#include <sys/apparmor.h>

#define DISABLE_APPARMOR "DISABLE_APPARMOR"
#define PROFILE_NAME "aa-chroot"

static char *union_mount_point;
static char **command_args;
static char *disable_apparmor;

static bool is_apparmor_enabled(void)
{
    if ((disable_apparmor != NULL)
            && (strncmp("y", disable_apparmor, 1) == 0)) {
        return false;
    }
    return true;
}

static void load_apparmor_profile(void)
{
    int ret;
    if (is_apparmor_enabled()) {
		ret = aa_change_onexec(PROFILE_NAME);
		if (ret < 0) {
			fprintf(stderr, "aa_change_onexec: %m\n");
			_exit(EXIT_FAILURE);
		}
	}
}

static int setup_container(void)
{
	int ret;
	int flags;

	/*
	 * use our own namespace for IPC, networking, mount points and uts
	 * (hostname and domain name), the pid namespace will be set up only
	 * after we have no more fork() (read: system()) calls to do for the
	 * setup
	 */
	flags = CLONE_FILES | CLONE_NEWIPC | CLONE_NEWNET |
			CLONE_NEWNS | CLONE_NEWUTS | CLONE_SYSVSEM;
	ret = unshare(flags);
	if (ret < 0) {
		ret = -errno;
		fprintf(stderr, "unshare: %m\n");
		return ret;
	}

	/*
	 * although we have a new mount points namespace, it is still necessary
	 * to make them private, recursively, so that changes aren't propagated
	 * to the parent namespace
	 */
	ret = mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL);
	if (ret < 0) {
		ret = -errno;
		fprintf(stderr, "cannot make \"/\" private: %m\n");
		return ret;
	}

	return 0;
}

static int setup_chroot(void)
{
	int chroot_ret;
	int chdir_ret;

	/*
	 * coverity groans about chdir not being called before we read the errno
	 * value, which triggers implicitly a call to __errno_location()
	 * hence, we don't retrieve this value before calling chdir, but because
	 * of this, the corresponding errno is lost
	 */
	chroot_ret = chroot(union_mount_point);
	chdir_ret = chdir("/");
	if (chroot_ret == -1) {
		/* dummy value, since errno is overwritten by chdir_ret */
		chroot_ret = -ECANCELED;
		fprintf(stderr, "chroot in %s failed, unknown error",
				union_mount_point);
		return chroot_ret;
	}
	if (chdir_ret < 0) {
		chdir_ret = -errno;
		fprintf(stderr, "chdir(/): %m\n");
		return chdir_ret;
	}

	return 0;
}

static void launch_pid_1(void)
{
	int ret;

	ret = setup_chroot();
	if (ret < 0) {
		fprintf(stderr, "setup_chroot: %m\n");
		_exit(EXIT_FAILURE);
	}

	fprintf(stderr, "%s\n", __func__);

	ret = execv(command_args[0], command_args);
	if (ret < 0)
		fprintf(stderr, "execv \"%s\": %m\n", command_args[0]);

	_exit(EXIT_FAILURE);
}

int main(int argc, char *argv[])
{
	int ret;
	pid_t pid;
	int status;

    if (argc < 3)
        fprintf(stderr, "wrong number of arguments : %d\n", argc);

    union_mount_point = argv[1];
    command_args = &argv[2];
    disable_apparmor = getenv(DISABLE_APPARMOR);

	ret = setup_container();
	if (ret < 0) {
		fprintf(stderr, "setup_container: %m\n");
		_exit(EXIT_FAILURE);
	}

	/*
	 * at last, setup the pid namespace, no more fork allowed apart from
	 * pid 1
	 */
	ret = unshare(CLONE_NEWPID);
	if (ret < 0) {
		fprintf(stderr, "unshare: %m\n");
		_exit(EXIT_FAILURE);
	}

    load_apparmor_profile();

	pid = fork();
	if (pid < 0) {
		fprintf(stderr, "fork: %m\n");
		_exit(EXIT_FAILURE);
	}
	if (pid == 0)
		launch_pid_1();

	ret = waitpid(pid, &status, 0);
	if (ret < 0) {
		_exit(EXIT_FAILURE);
		fprintf(stderr, "waitpid: %m\n");
	}
	if (WIFEXITED(status))
		fprintf(stderr, "program exited with status %d\n", WEXITSTATUS(status));

	fprintf(stderr, "instance terminated with status %d\n", status);

	_exit(EXIT_SUCCESS);
}
profile aa-chroot flags=(attach_disconnected,chroot_relative) {

#{
# flags=(attach_disconnected,mediate_deleted) {
# firmwared will load it, with "=%S1%\nprofile %S2% %S3% " prepended,
# with :
#   S1: the base workspace directory for the instance, containing the ro, rw,
#       union and workdir directories / mountpoints
#   S2: the instance unique name
#   S3: the content of this very file




  signal,
  mount,
  network,
  #capability,

   capability sys_chroot,
   capability sys_admin,
   capability chown,
   capability dac_override,
   capability dac_read_search,
   capability fowner,
   capability fsetid,
   capability kill,
   capability setgid,
   capability setuid,
   capability setpcap,
   capability linux_immutable,
   capability net_bind_service,
   capability net_broadcast,
   capability net_admin,
   capability net_raw,
   capability ipc_lock,
   capability ipc_owner,
   capability sys_module,
   capability sys_rawio,
   capability sys_chroot,
   capability sys_ptrace,
   capability sys_pacct,
   capability sys_admin,
   capability sys_boot,
   capability sys_nice,
   capability sys_resource,
   capability sys_tty_config,
   capability mknod,
   capability lease,
   capability audit_write,
   capability audit_control,
   capability setfcap,
   capability mac_override,
   capability mac_admin,
   capability syslog,


  file,
  #umount,

   audit deny /bin/* lrwxk,
}
all: aa-chroot

aa-chroot: aa-chroot.c
        gcc -o aa-chroot aa-chroot.c -lapparmor
-- 
AppArmor mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/apparmor

Reply via email to