anatol / booster

Fast and secure initramfs generator
MIT License
506 stars 45 forks source link

init: use libfido2 when recovering fido2 password #240

Open c3Ls1US opened 1 year ago

c3Ls1US commented 1 year ago

Uses libfido2 when validating hidraw devices rather than reading from /sys/class/hidraw, and more precisely resolves the timing issue for them when recovering the fido2 password from https://github.com/anatol/booster/commit/31d06a47708a139c159cb96dc7dc067a64230db0.

It works but still a WIP as fido2-assert is still used for unlocking here, and I'd favor using the library directly instead

c3Ls1US commented 1 year ago

Still a WIP. Strangely, when recovering the HMAC secret using the library, the value is different than the one recovered via fido2-assert even after it passes assertion. I have to look into it more later, but you can see this when comparing the hashes. But it could just be that my inputs are wrong, like perhaps the client data hash?

Debug Log
Aug 15 19:01:14 archlinux booster: password from systemd-fido2 token #0 matches
Aug 15 19:01:14 archlinux booster: recovered password from systemd-fido2 token #0
Aug 15 19:01:14 archlinux booster: sha256: 1ae2bc586fa456bed4cd6d4f9dc2fc472b1402c268295171e724985ce76ba482
Aug 15 19:01:14 archlinux booster: used fido2-assert to retrieve hmac secret for hidraw1
Aug 15 19:01:14 archlinux booster: running fido2-assert tool for hidraw1
Aug 15 19:01:14 archlinux booster: sha256: 2eaf7579d990625ab8e588892b6fc92c57c9d57fe78d5f8b437bd145d01557e0
Aug 15 19:01:14 archlinux booster: used libfido2 when retrieving hmac secret for hidraw1
anatol commented 1 year ago

Using an existing library for some functionality (e.g. fido2 handling) is a preferable way in general.

The only issue I see here is that libfido will always be linked to booster init and thus will be always pulled to all images. Given that fido2 functionality is used relatively infrequent I think it makes sense to move it to some optional library that added to the image only when user enables fido2 support.

For this booster needs to move such extra functionality to something like go plugins https://pkg.go.dev/plugin

c3Ls1US commented 1 year ago

Using an existing library for some functionality (e.g. fido2 handling) is a preferable way in general.

The only issue I see here is that libfido will always be linked to booster init and thus will be always pulled to all images. Given that fido2 functionality is used relatively infrequent I think it makes sense to move it to some optional library that added to the image only when user enables fido2 support.

For this booster needs to move such extra functionality to something like go plugins https://pkg.go.dev/plugin

I agree with you, and I'll look into it!

c3Ls1US commented 1 year ago

I fixed the bug. At it's current state, only the library's functionality is used rather than fido2-assert. User presence works, but I haven't tested unlocking with PINs (user verification) yet

c3Ls1US commented 1 year ago

I looked into go plugins https://pkg.go.dev/plugin. While extending the libfido2 functionality as a plugin promotes a modular design, thus avoiding the hard dependency to the images, things gets rather complicated. Namely, the CGO runtime will be involved in the init process and reasoning about the program can be more difficult. The plugin maintainers warn about this too:

Reasoning about program initialization is more difficult when some packages may not be initialized until long after the application has started running.

Converting this PR as a draft for now

anatol commented 1 year ago

While extending the libfido2 functionality as a plugin promotes a modular design, thus avoiding the hard dependency to the images, things gets rather complicated.

Bummer.

Are there other ways to decouple libfido2 from the booster? Like compiling booster-libfido2 as a separate binary, invoke it from booster and use stdin pipe for communication maybe?

c3Ls1US commented 1 year ago

Are there other ways to decouple libfido2 from the booster?

I think decoupling will involve either dynamically linking in some other way or compiling as a separate binary like you said. Alternatively, a pure Go library would be great too but there doesn't seem to be any

Like compiling booster-libfido2 as a separate binary, invoke it from booster and use stdin pipe for communication maybe?

I could just do that and in some ways, it's better because it involves no additional runtime and dependency. Though, the status quo wouldn't really change since os/exec would still be used and essentially we'd be creating a fido2-assert but more customized/tailored. Still, it may be worth it

c3Ls1US commented 1 year ago

@anatol I fixed the bug and was able to get libfido2 to work as a plugin. Now, it needs review

booster.yaml

extra_files: /usr/lib/booster/libfido2_plugin.so
c3Ls1US commented 1 year ago

@anatol Finished. I don't plan on adding anything, so let me know if I am missing/or should add anything more.

I also tested (locally) the PIN code path and can confirm it works. However, I haven't tested UV because my key doesn't support it. For Yubikeys, I think the 5 series supports UV but not the security key series (which is what I use).

booster.yaml

extra_files: /usr/lib/booster/libfido2_plugin.so

PKGBUILD:

pkgname=booster-git
pkgver=0.11.r58.gde972d3
pkgrel=1
pkgdesc='Fast and secure initramfs generator'
arch=(x86_64)
url='https://github.com/c3Ls1US/booster'
license=(MIT)
depends=(bash glibc)
makedepends=(git go ruby-ronn-ng libfido2) 
#checkdepends=(qemu-headless linux tang)
optdepends=(
  'busybox: to enable emergency shell at the boot time'
  'yubikey-personalization: for clevis Yubikey challenge-response support'
  'libfido2: for FIDO2 unlocking encrypted partitions enrolled via systemd-cryptenroll'
)
backup=(etc/booster.yaml)
provides=(booster initramfs)
conflicts=(booster)
replaces=(booster)
source=(git+https://github.com/c3Ls1US/booster.git#branch=fido2)
sha512sums=('SKIP')

pkgver() {
  cd booster
  git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g'
}

build() {
  cd booster

  cd generator

  CFLAGS="-march=x86-64-v3 -mtune=generic -O2 -pipe -fno-plt -fexceptions -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security -fstack-clash-protection -fcf-protection" 

  CGO_CPPFLAGS="${CPPFLAGS}" \
  CGO_CFLAGS="${CFLAGS}" \
  CGO_CXXFLAGS="${CXXFLAGS}" \
  CGO_LDFLAGS="${LDFLAGS}" \
  go build -trimpath -buildmode=pie -mod=readonly -modcacherw -ldflags "-linkmode external -extldflags \"${LDFLAGS}\""

  # Build the plugin
  cd ../init/plugins
  CGO_CPPFLAGS="${CPPFLAGS}" \
  CGO_CFLAGS="${CFLAGS}" \
  CGO_CXXFLAGS="${CXXFLAGS}" \
  CGO_LDFLAGS="${LDFLAGS}" \
  CGO_ENABLED=1 go build -buildmode=plugin -o libfido2_plugin.so -trimpath -mod=readonly -modcacherw -ldflags "-linkmode external -extldflags \"${LDFLAGS}\"" libfido2_plugin.go

  # Move the plugin shared object file
  mv libfido2_plugin.so ../

  # Build init
  cd ..
  CGO_CPPFLAGS="${CPPFLAGS}" \
  CGO_CFLAGS="${CFLAGS}" \
  CGO_CXXFLAGS="${CXXFLAGS}" \
  CGO_LDFLAGS="${LDFLAGS}" \
  CGO_ENABLED=1 go build -trimpath -buildmode=pie -mod=readonly -modcacherw -ldflags "-linkmode external -extldflags \"${LDFLAGS}\""

  cd ..
  ronn docs/manpage.md
}

check() {
  cd booster/tests
  # arch chroot does not allow access to KVM
  # TEST_DISABLE_KVM=1 go test -v # integration tests require a lot of time and space to build 10G images
}

package() {
  cd booster
  mkdir "$pkgdir/etc/"
  touch "$pkgdir/etc/booster.yaml"
  install -Dp -m755 generator/generator "$pkgdir/usr/bin/booster"
  install -Dp -m644 docs/manpage.1 "$pkgdir/usr/share/man/man1/booster.1"
  install -Dp -m755 init/init "$pkgdir/usr/lib/booster/init"
  install -Dp -m755 init/libfido2_plugin.so "$pkgdir/usr/lib/booster/libfido2_plugin.so"
  install -Dp -m755 packaging/arch/regenerate_images "$pkgdir/usr/lib/booster/regenerate_images"

  install -Dp -m644 packaging/arch/90-booster-install.hook "$pkgdir/usr/share/libalpm/hooks/90-booster-install.hook"
  install -Dp -m755 packaging/arch/booster-install "$pkgdir/usr/share/libalpm/scripts/booster-install"
  install -Dp -m644 packaging/arch/60-booster-remove.hook "$pkgdir/usr/share/libalpm/hooks/60-booster-remove.hook"
  install -Dp -m755 packaging/arch/booster-remove "$pkgdir/usr/share/libalpm/scripts/booster-remove"
  install -Dp -m755 contrib/completion/bash "$pkgdir/usr/share/bash-completion/completions/booster"
}

c3Ls1US commented 1 year ago

@anatol I'm sorta having second thoughts about this PR to just prefer the current implementation instead, despite it not being pretty

Both accomplish the same effect, but using the library directly seems to add little while adding more complexity--the build process, install size, additional code, CGO--and risks

The last point in particular is a concern, as the init would be less secure because of the involvement of C code