Skip to content
Merged
79 changes: 44 additions & 35 deletions recovery/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ package recovery

import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"path"
Expand Down Expand Up @@ -52,22 +52,9 @@ func (d *DiskDevice) FindFromPartLabel(label string) error {

func (d *DiskDevice) CreatePartition(size uint64, label string) error {
logger.Noticef("Create partition %q", label)
cmd := exec.Command("sfdisk", "--no-reread", "-a", d.node)
stdin, err := cmd.StdinPipe()
if err != nil {
return err
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("cannot create partition: %s", err)
}
if _, err := stdin.Write([]byte(fmt.Sprintf(",%d,,,", size/sizeSector))); err != nil {
return fmt.Errorf("cannot write to sfdisk pipe: %s", err)
}
if err := stdin.Close(); err != nil {
return fmt.Errorf("cannot close fdisk pipe: %s", err)
}
if err := cmd.Wait(); err != nil {
return fmt.Errorf("cannot run sfdisk: %s", err)
if output, err := pipeRun([]byte(fmt.Sprintf(",%d,,,", size/sizeSector)),
"sfdisk", "--no-reread", "-a", d.node); err != nil {
return osutil.OutputErr(output, fmt.Errorf("cannot create partition: %s", err))
}

// Re-read partition table
Expand All @@ -85,12 +72,12 @@ func (d *DiskDevice) CreatePartition(size uint64, label string) error {
return nil
}

func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyfile string, keyfileSize int, cryptdev string) error {
func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyBuffer []byte, cryptdev string) error {
logger.Noticef("Create partition %q", label)
cmd := exec.Command("sfdisk", "--no-reread", "-a", d.node)
stdin, err := cmd.StdinPipe()
if err != nil {
return err
return fmt.Errorf("cannot get sfdisk stdin: %s", err)
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("cannot create partition: %s", err)
Expand All @@ -115,34 +102,27 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyfile stri
// FIXME: determine partition name in a civilized way
partdev := d.partDev(4)

// Set up LUKS device
logger.Noticef("Create LUKS keyfile")
buffer := make([]byte, keyfileSize)
rand.Read(buffer)
if err := ioutil.WriteFile(keyfile, buffer, 0400); err != nil {
return fmt.Errorf("cannot create keyfile %s: %s", keyfile, err)
}

// Don't remove this delay, prevents kernel crash
// Don't remove this delay, it prevents a kernel crash
// see https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1835279
time.Sleep(1 * time.Second)

// Write key to stdin to avoid creating a temporary file
logger.Noticef("Create LUKS device on %s", partdev)
if output, err := exec.Command("cryptsetup", "-q", "--type", "luks2", "--key-file", keyfile,
"--pbkdf-memory", "100000", "luksFormat", partdev).CombinedOutput(); err != nil {
return osutil.OutputErr(output, fmt.Errorf("cannot format LUKS device on %s: %s", partdev, err))
if output, err := pipeRun(keyBuffer, "cryptsetup", "-q", "--type", "luks2", "--key-file", "-",
"--pbkdf-memory", "100000", "luksFormat", partdev); err != nil {
return osutil.OutputErr(output, fmt.Errorf("cannot format LUKS device: %s", err))
}

time.Sleep(1 * time.Second)

logger.Noticef("Open LUKS device")
if output, err := exec.Command("sh", "-c", fmt.Sprintf("LD_PRELOAD=/lib/no-udev.so cryptsetup "+
"--type luks2 --key-file %s --pbkdf-memory 100000 open %s %s", keyfile, partdev,
path.Base(cryptdev))).CombinedOutput(); err != nil {
if output, err := pipeRun(keyBuffer, "sh", "-c", fmt.Sprintf("LD_PRELOAD=/lib/no-udev.so cryptsetup "+
"--type luks2 --key-file - --pbkdf-memory 100000 open %s %s", partdev,
path.Base(cryptdev))); err != nil {
return osutil.OutputErr(output, fmt.Errorf("cannot open LUKS device on %s: %s", partdev, err))
}

// Ok, now this is ugly. We'll have to see how to handle this properly without udev.
// FIXME: Ok, now this is ugly. We'll have to see how to handle this properly.
logger.Noticef("Hack: create LUKS device symlink")
if err := os.Symlink("../dm-0", cryptdev); err != nil {
return fmt.Errorf("cannot create LUKS device symlink: %s", err)
Expand All @@ -159,6 +139,35 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyfile stri
return nil
}

func pipeRun(input []byte, name string, args ...string) ([]byte, error) {
cmd := exec.Command(name, args...)
stdin, err := cmd.StdinPipe()
var out bytes.Buffer
// FIXME: use combined output
cmd.Stderr = &out
if err != nil {
return nil, err
}
if err = cmd.Start(); err != nil {
return nil, err
}
n, err := stdin.Write(input)
if err != nil {
return nil, err
}
if n != len(input) {
err = fmt.Errorf("short write (%d)", n)
return nil, err
}
if err = stdin.Close(); err != nil {
return nil, err
}
if err = cmd.Wait(); err != nil {
return nil, err
}
return out.Bytes(), nil
}

func createCrypttab(partdev, keyfile, cryptdev string) error {
buffer := fmt.Sprintf("%s %s %s luks\n", cryptdev, partdev, keyfile)
if err := ioutil.WriteFile(keyfile, []byte(buffer), 0644); err != nil {
Expand Down
68 changes: 58 additions & 10 deletions recovery/recovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@
package recovery

import (
"crypto/rand"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"syscall"
"time"

"github.com/chrisccoulson/ubuntu-core-fde-utils"

"github.com/snapcore/snapd/bootloader"
"github.com/snapcore/snapd/bootloader/grubenv"
"github.com/snapcore/snapd/logger"
Expand Down Expand Up @@ -142,8 +146,6 @@ func Install(version string) error {
mntSysRecover := "/mnt/sys-recover"
mntSystemBoot := "/mnt/system-boot"

keyfile := path.Join(mntSystemBoot, "keyfile")

if err := mountFilesystem("ubuntu-boot", mntSystemBoot); err != nil {
return err
}
Expand All @@ -152,8 +154,16 @@ func Install(version string) error {

cryptdev := "ubuntu-data"

logger.Noticef("Create LUKS key")
keySize := 4 * sizeKB
keyBuffer := make([]byte, keySize)
n, err := rand.Read(keyBuffer)
if n != keySize || err != nil {
return fmt.Errorf("cannot create LUKS key: %s", err)
}

logger.Noticef("Install recovery %s", version)
if err := createWritable(keyfile, 4*sizeKB, cryptdev); err != nil {
if err := createWritable(keyBuffer, cryptdev); err != nil {
logger.Noticef("cannot create writable: %s", err)
return err
}
Expand All @@ -175,6 +185,29 @@ func Install(version string) error {
return err
}

logger.Noticef("Create lockout authorization")
lockoutAuth := make([]byte, 16)
n, err = rand.Read(lockoutAuth)
if n != 16 || err != nil {
return fmt.Errorf("cannot create lockout authorization: %s", err)
}

logger.Noticef("Provisioning the TPM")
if err := fdeutil.ProvisionTPM(lockoutAuth); err != nil {
logger.Noticef("error provisioning the TPM: %s", err)
return fmt.Errorf("cannot provision TPM: %s", err)
}

if err := storeKeyfile(mntSystemBoot, keyBuffer); err != nil {
return fmt.Errorf("cannot store keyfile: %s", err)
}

if err := storeLockoutAuth(mntWritable, lockoutAuth); err != nil {
return fmt.Errorf("cannot store lockout authorization: %s", err)
}

syscall.Sync()

if err := umount(mntWritable); err != nil {
return err
}
Expand All @@ -196,15 +229,15 @@ func Install(version string) error {
return nil
}

func createWritable(keyfile string, keyfileSize int, cryptdev string) error {
logger.Noticef("Creating new writable")
func createWritable(keyBuffer []byte, cryptdev string) error {
logger.Noticef("Creating new ubuntu-data partition")
disk := &DiskDevice{}
if err := disk.FindFromPartLabel("ubuntu-boot"); err != nil {
return fmt.Errorf("cannot determine boot device: %s", err)
}

// FIXME: get values from gadget, system
err := disk.CreateLUKSPartition(1000*sizeMB, "ubuntu-data", keyfile, keyfileSize, cryptdev)
err := disk.CreateLUKSPartition(1000*sizeMB, "ubuntu-data", keyBuffer, cryptdev)
if err != nil {
return fmt.Errorf("cannot create new ubuntu-data: %s", err)
}
Expand All @@ -226,6 +259,23 @@ func mountFilesystem(label string, mountpoint string) error {
return nil
}

func storeKeyfile(dir string, buffer []byte) error {
// TODO: seal keyfile
if err := ioutil.WriteFile(path.Join(dir, "keyfile"), buffer, 0400); err != nil {
return err
}

// Don't remove this sync, it prevents file corruption on vfat
syscall.Sync()

return nil
}

// The lockout authorization file is stored in the encrypted partition
func storeLockoutAuth(dir string, lockoutAuth []byte) error {
return ioutil.WriteFile(path.Join(dir, "system-data/lockoutAuth"), lockoutAuth, 0400)
}

func updateRecovery(mntWritable, mntSysRecover, mntSystemBoot, version string) (core string, kernel string, err error) {
logger.Noticef("Populating new writable")

Expand Down Expand Up @@ -326,10 +376,8 @@ func extractKernel(kernelPath, mntSystemBoot string) error {
}
}

// Don't remove this sync, prevents corrupted files on vfat
if err := exec.Command("sync").Run(); err != nil {
return fmt.Errorf("cannot sync filesystems: %s", err)
}
// Don't remove this sync, it prevents file corruption on vfat
syscall.Sync()

if err := umount(mntKernelSnap); err != nil {
return err
Expand Down
14 changes: 14 additions & 0 deletions vendor/vendor.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
"path": "context",
"revision": ""
},
{
"checksumSHA1": "X9uJjtPte7hdg0/LSEtnDD4v6Iw=",
"path": "github.com/chrisccoulson/ubuntu-core-fde-utils",
"revision": "49b2130cfa1a95f940eaf348b3a71b3f7af344ba",
"revisionTime": "2019-07-11T08:52:23Z"
},
{
"checksumSHA1": "zg16zjZTQ9R89+UOLmEZxHgxDtM=",
"path": "github.com/coreos/go-systemd/activation",
Expand All @@ -24,6 +30,14 @@
"revision": "97646858c46433e4afb3432ad28c12e968efa298",
"revisionTime": "2017-08-22T15:24:03Z"
},
{
"checksumSHA1": "mkW1FNv2atdJblBoNsGkAfvec5w=",
"origin": "github.com/chrisccoulson/go-tpm",
"path": "github.com/google/go-tpm",
"revision": "c6c7cb7465ae50e13263f2f8ff33f57d1f9859bc",
"revisionTime": "2019-07-08T13:36:22Z",
"tree": true
},
{
"checksumSHA1": "iIUYZyoanCQQTUaWsu8b+iOSPt4=",
"path": "github.com/gorilla/context",
Expand Down