LINUX.ORG.RU
решено ФорумAdmin

dm-crypt без root

 , ,


0

1

Дано:

Дамп LUKS контейнера в luks.img, поддержка пространств имён, FUSE.

Задача:

Считать данные из дампа.

Ограничения:

  • Нельзя использовать SUID бинарники;

  • Нельзя использовать привилегированные демоны.

    Насколько я понимаю, условный udisks повышает привилегии через polkit, а посему не подходит;

Что я пытался делать:

Искал реализацию LUKS/device-mapper через FUSE - не нашёл.

Почему никто не реализовал нечто подобное? В том смысле, с какими трудностями столкнулись другие люди?



Последнее исправление: oeltra (всего исправлений: 1)
    polkit.addRule(function(action, subject) {
        if ((action.id == "org.freedesktop.udisks2.filesystem-mount-system" || action.id == "org.freedesktop.udisks2.filesystem-mount") && subject.isInGroup("wheel")) {
            return polkit.Result.YES;
        }
    });
rtxtxtrx ★★★
()
Ответ на: комментарий от rtxtxtrx

polkit - механизм повышения привилегий.

Смысл в том, чтобы считать данные без повышения привилегий.

Меня очень огорчает, что, условно, файл БД keepassxc я могу без повышения привилегий монтировать как файловую систему через FUSE, а LUKS контейнер - нет.

Казалось бы:

  • Я владелец файла;
  • В системе включен механизм пространств имён, что значит, что:
    • Я могу «прикинуться» root посредством user namespace.

      Необходимые subuid/subgid есть.

    • Я могу монтировать некоторые файловые системы в mount namespace;

    • Я могу представлять произвольные ресурсы в виде файловой системы благодаря FUSE. Один curlftpfs чего стоит.

      Если мне не изменяет память, для FUSE применимо FS_USERNS_MOUNT;

Но LUKS контейнер я, судя по всему, без привилегий, открыть не могу.

Это очень разочаровывает.

Особенно учитывая, что создать контейнер можно:

fallocate -l 1G vol.img
cryptsetup luksFormat vol.img

100% можно как-то открыть контейнер без привилегий. Ну не верю я, что нельзя.

oeltra
() автор топика
Ответ на: комментарий от oeltra

файл БД keepassxc я могу без повышения привилегий монтировать как файловую систему через FUSE, а LUKS контейнер - нет.

Что-то вы обманываете. Любая операция монтирования требует рутовых прав. Есть исключение в виде записей в fstab с опцией user, однако же запись в fstab обычно доступна только root

cobold ★★★★★
()
Последнее исправление: cobold (всего исправлений: 1)
Ответ на: комментарий от cobold

man mount_namespaces.

TL;DR: глобально, само собой, смонтировать без root нельзя, но в рамках одного процесса (древа) - можно (если у файловой системы есть флаг FS_USERNS_MOUNT в исходниках).

Например, таким кустарным образом я сохранял ссылки на сетевые пространства имён в родителе:

# примечание: пишу по памяти, код не запускал
unshare -rm
mount --mkdir -t tmpfs none /tmp/root
touch /tmp/root/netnsA
unshare --net=/tmp/root/netnsA /usr/bin/true

Как работает FUSE, честно, забыл маленько, но на постоянной основе использую adbfs-rootless.

FUSE не требует polkit. Записи в /etc/fstab или чего-то подобного нет.

adbfs -o rescan /произвольный/путь/в/home

работает без повышения привилегий, насколько я помню. Если есть телефон под рукой - проверьте.

oeltra
() автор топика
Ответ на: комментарий от oeltra

Меня очень огорчает, что, условно, файл БД keepassxc я могу без повышения привилегий монтировать как файловую систему через FUSE, а LUKS контейнер - нет.

100% можно как-то открыть контейнер без привилегий. Ну не верю я, что нельзя.

Возможно дело в том, что до монтирования файловой системы требуется создать device mapper, который тоже требует привилегий.

LLM-9000
()
Ответ на: комментарий от rtxtxtrx

Вот здесь несколько странный момент: если добавить пользователя в группу disk, то пользователь получает возможность считывать/записывать блочные устройства (например, /dev/sda).

В том числе, пользователь может создавать loopback-устройства посредством losetup --find --show vol.img.

Если vol.img - ext4, то монтировать loop0 не выйдет из-за отсутствия флага FS_USERNS_MOUNT в исходниках ext4 (кинет permission_denied на этапе проверки capability).

Можно монтировать через FUSE посредством ext4fuse, например.

cryptsetup open vol.img vol вне пространства имен падает с:

Cannot initialize device-mapper, running as non-root user.
Cannot use device vol, name is invalid or still in use.

и даже в пространстве имён, созданным посредством unshare -rm:

Cannot initialize device-mapper. Is dm_mod kernel module loaded?
Cannot use device vol, name is invalid or still in use.
Enter passphrase for vol.img: 
Cannot initialize device-mapper. Is dm_mod kernel module loaded?
Cannot use device vol, name is invalid or still in use.

и если заранее создать блочное устройство (вне зависимости от пространства имён):

cryptsetup luksFormat vol.img
...
losetup --show --find vol.img
...
cryptsetup open /dev/loop0 vol
Failed to acquire read lock on device /dev/loop0.
Device /dev/loop0 is not a valid LUKS device.
oeltra
() автор топика
Ответ на: комментарий от anonymous

У меня уже есть дамп контейнера LUKS с ext4 внутри (не суть).

Меня не интересуют

gocryptfs, cryfs и прочие securefs

в рамках данного вопроса.

Касательно самостоятельной реализации, процитирую себя:

Почему никто не реализовал нечто подобное? В том смысле, с какими трудностями столкнулись другие люди?

oeltra
() автор топика
Ответ на: комментарий от oeltra

с какими трудностями столкнулись другие люди

необходимость реализации километров ядерного кода заново в userpace и отсутствии квалификации того, кому это надо?

Pinkbyte ★★★★★
()
Последнее исправление: Pinkbyte (всего исправлений: 1)
Ответ на: комментарий от oeltra

UPD: да, память мне не изменяет. Часть файла fs/fuse/inode.c, коммит cec1e6e5d1ab33403b809f79cd20d6aff124ccfe:

...
static struct file_system_type fuse_fs_type = {
	.owner		= THIS_MODULE,
	.name		= "fuse",
	.fs_flags	= FS_HAS_SUBTYPE | FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
	.init_fs_context = fuse_init_fs_context,
	.parameters	= fuse_fs_parameters,
	.kill_sb	= fuse_kill_sb_anon,
};
MODULE_ALIAS_FS("fuse");
...
oeltra
() автор топика
Ответ на: комментарий от Pinkbyte

необходимость реализации километров ядерного кода заново в userpace

А зачем? Криптография вся есть готовая, LUKS это в принципе user space. Ту часть, за которую отвечает device-mapper всё равно придётся делать как-то по-другому. Думаю, тут основная головная боль — файловая система поверх зашифрованного образа. Это либо затягивать к себе целиком fuse2fs, либо как-то корячить FUSE поверх FUSE, что, если и будет работать, явно ещё хуже, чем в одн слой.

Думаю, тут просто не пересеклись множества тех, кому это надо и тех, кто может и хочет сделать такую вещь.

anonymous
()
Ответ на: комментарий от anonymous

Думаю, тут просто не пересеклись множества тех, кому это надо и тех, кто может и хочет сделать такую вещь.

Да, так я имел ввиду скорее это, согласен

Pinkbyte ★★★★★
()
Ответ на: комментарий от Pinkbyte

Зачем его реализовывать? Любой ядерный код прекрасно запускается в юзерспейсе через обычный советский qemu. То, что автору это почему-то не нравится, это проблемы автора. А так - запустил, открыл, заэкспортировал расшифрованное устройство через любой сетевой протокол и всё. Никто и не заметит.

vbr ★★★★★
()
Ответ на: комментарий от LLM-9000

Да, работает.

Что огорчает - это необходимость в отдельной rootfs.

Если кому в будущем понадобится, процесс тупняка проходил примерно так:

#!/usr/bin/env sh

DISK=disk.img
DISK_SZ=4G
MNT=m
TERMINAL=kitty
INIT=/lib/systemd/systemd
PASSWD="$( perl -e 'print crypt("root","\$6\$saltsalt\$")' )"

cleanup() {
  umount -- "$MNT"
}
trap 'trap -; cleanup' INT HUP TERM

die() {
  E="$?"

  if [ -z "$*" ]
  then
    echo exiting... >&2
  else
    echo "$@" >&2
  fi
  exit "$E"
}
diec() {
  E="$1"
  shift

  if [ -z "$*" ]
  then
    echo exiting... >&2
  else
    echo "$@" >&2
  fi
  exit "$E"
}

launch() {
    #ubd0=./disk.img root=/dev/ubda rw \
  "$TERMINAL" -e vmlinux \
    rootfstype=hostfs rootflags="$PWD/rootfs" rw \
    ubd1=./vol.img \
    mem=2048M \
    init="$INIT" \
    con1=fd:0,fd:1 \
    single
}
mount_disk() {
  if mountpoint -q -- "$MNT"
  then
    echo something is already mounted at "$MNT" >&2
    return 1
  fi
  if ! fuse2fs "$DISK" "$MNT" -o fakeroot >/dev/null
  then
    echo failed to mount disk >&2
    return 1
  fi
}


case "$@" in
  disk)
    if fallocate -l "$DISK_SZ" -- "$DISK" && mkfs.ext4 -q -- "$DISK"
    then
      :
    else
      echo failed to initialize disk >&2
      diec 1
    fi
    ;;

  fstab)
    if mount_disk
    then
      mkdir -p "$MNT/etc"
      cat - <<-EOF >"$MNT/etc/fstab"
	/dev/ubda / root defaults 0 0
	EOF
      sed -i "s|^root:.*\$|root:$PASSWD:0::::::|" "$MNT/etc/shadow"
      cleanup
    else
      diec 1
    fi
    ;;

  *)
    if [ -f "$DISK" ] && mount_disk
    then
      launch
      cleanup
    else
      diec 1 failed to launch UML
    fi
esac
oeltra
() автор топика

Ну и самый примитивный вариант, который можно вспомнить по памяти, просто чтоб было:

#!/usr/bin/env sh

ISO=systemrescue-12.02-amd64.iso

printf '%s\n' 'mount -t 9p -o trans=virtio shared /shared --mkdir'
qemu-system-x86_64 \
   -enable-kvm -cpu host \
   -vga virtio \
   -virtfs local,path=shared,mount_tag=shared,security_model=mapped-xattr \
   -m 2048 \
   ${ISO:+ -cdrom "$ISO" }
oeltra
() автор топика