From 287d27f2b73f2af95609a38205c60850ffdadb97 Mon Sep 17 00:00:00 2001 From: Claudio Matsuoka Date: Tue, 9 Jul 2019 16:05:40 -0300 Subject: [PATCH 1/9] recovery: integrate TPM provisioning Execute TPM provisioning on install using Chris Coulson's FDE utils code from https://github.com/chrisccoulson/ubuntu-core-fde-utils. This also requires https://github.com/google/go-tpm/pull/109 which has been added to vendored packages. Try to provision the TPM as late as possible to prevent a situation where the installation fails after the TPM is provisioned. Provisioning happens just before sealing the LUKS device keyfile and the lockout authorization value is stored inside the encrypted partition. Signed-off-by: Claudio Matsuoka --- recovery/device.go | 26 ++++---- recovery/fdeutils/provisioning.go | 100 ++++++++++++++++++++++++++++++ recovery/recovery.go | 66 +++++++++++++++++--- vendor/vendor.json | 7 +++ 4 files changed, 180 insertions(+), 19 deletions(-) create mode 100644 recovery/fdeutils/provisioning.go diff --git a/recovery/device.go b/recovery/device.go index a43a69125ec..c95186fc6bf 100644 --- a/recovery/device.go +++ b/recovery/device.go @@ -22,7 +22,6 @@ import ( "bufio" "fmt" "io/ioutil" - "math/rand" "os" "os/exec" "path" @@ -85,7 +84,7 @@ 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() @@ -115,18 +114,16 @@ 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) + // Create a temporary unsealed keyfile on a RAM-backed filesystem + keyfile := "/run/tmpkeyfile" + if err := ioutil.WriteFile(keyfile, keyBuffer, 0400); err != nil { + return fmt.Errorf("cannot create keyfile: %s", err) + } + 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 { @@ -142,7 +139,12 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyfile stri 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: use secure delete + if err := os.Remove(keyfile); err != nil { + logger.Noticef("can't remove keyfile: %s", err) + } + + // 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) diff --git a/recovery/fdeutils/provisioning.go b/recovery/fdeutils/provisioning.go new file mode 100644 index 00000000000..e2b62a8733a --- /dev/null +++ b/recovery/fdeutils/provisioning.go @@ -0,0 +1,100 @@ +package fdeutils + +import ( + "errors" + "fmt" + "os" + + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" +) + +const ( + srkHandle tpmutil.Handle = 0x81000000 + + tpmPath string = "/dev/tpm0" + ppiPath string = "/sys/class/tpm/tpm0/ppi/request" + + permanentProps uint32 = 0x00000200 + lockoutAuthSet uint32 = 1 << 2 + disableClear uint32 = 1 << 8 + + clearPPIRequest string = "5" +) + +var ( + ErrClearRequiresPPI = errors.New("clearing requires the use of the Physical Presence Interface") + + srkTemplate = tpm2.Public{ + Type: tpm2.AlgRSA, + NameAlg: tpm2.AlgSHA256, + Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | + tpm2.FlagUserWithAuth | tpm2.FlagRestricted | tpm2.FlagDecrypt, + AuthPolicy: nil, + RSAParameters: &tpm2.RSAParams{ + Symmetric: &tpm2.SymScheme{ + Alg: tpm2.AlgAES, + KeyBits: 128, + Mode: tpm2.AlgCFB}, + KeyBits: 2048, + Exponent: 0, + ModulusRaw: make([]byte, 256)}} +) + +func ProvisionTPM(lockoutAuth []byte) error { + rw, err := tpm2.OpenTPM(tpmPath) + if err != nil { + return fmt.Errorf("failed to open TPM device: %v", err) + } + + c, _, err := tpm2.GetCapability(rw, tpm2.CapabilityTPMProperties, 1, permanentProps) + if err != nil { + return fmt.Errorf("failed to request permanent properties: %v", err) + } + + p := c[0].(tpm2.TaggedProperty).Value + if p&lockoutAuthSet > 0 || p&disableClear > 0 { + return ErrClearRequiresPPI + } + + if err := tpm2.Clear(rw, tpm2.HandleLockout, ""); err != nil { + return fmt.Errorf("failed to clear the TPM: %v", err) + } + + h, _, err := tpm2.CreatePrimary(rw, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate) + if err != nil { + return fmt.Errorf("failed to create storage root key: %v", err) + } + + if err := tpm2.EvictControl(rw, "", tpm2.HandleOwner, h, srkHandle); err != nil { + return fmt.Errorf("failed to make storage root key persistent: %v", err) + } + + if err := tpm2.SetDictionaryAttackParameters(rw, 32, 7200, 86400, ""); err != nil { + return fmt.Errorf("failed to configure DA parameters: %v", err) + } + + if err := tpm2.DisableOwnerClear(rw, ""); err != nil { + return fmt.Errorf("failed to disable owner clear: %v", err) + } + + if err := tpm2.HierarchyChangeAuth(rw, tpm2.HandleLockout, "", string(lockoutAuth)); err != nil { + return fmt.Errorf("failed to set the lockout hierarchy authorization value: %v", err) + } + + return nil +} + +func RequestTPMClearUsingPPI() error { + f, err := os.OpenFile(ppiPath, os.O_WRONLY, 0) + if err != nil { + return fmt.Errorf("failed to open request handle: %v", err) + } + defer f.Close() + + if _, err := f.WriteString(clearPPIRequest); err != nil { + return fmt.Errorf("failed to submit request: %v", err) + } + + return nil +} diff --git a/recovery/recovery.go b/recovery/recovery.go index 739f2d0ae1f..067a1989631 100644 --- a/recovery/recovery.go +++ b/recovery/recovery.go @@ -21,6 +21,7 @@ package recovery import ( + "crypto/rand" "fmt" "io/ioutil" "os" @@ -32,6 +33,7 @@ import ( "github.com/snapcore/snapd/bootloader/grubenv" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" + "github.com/snapcore/snapd/recovery/fdeutils" ) const ( @@ -142,8 +144,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 } @@ -152,8 +152,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 } @@ -175,6 +183,31 @@ 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 := fdeutils.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) + } + + if err := exec.Command("sync").Run(); err != nil { + return fmt.Errorf("cannot sync: %s", err) + } + if err := umount(mntWritable); err != nil { return err } @@ -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") 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) } @@ -226,6 +259,25 @@ 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 + if err := exec.Command("sync").Run(); err != nil { + return fmt.Errorf("cannot sync: %s", err) + } + + 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") @@ -326,7 +378,7 @@ func extractKernel(kernelPath, mntSystemBoot string) error { } } - // Don't remove this sync, prevents corrupted files on vfat + // Don't remove this sync, it prevents file corruption on vfat if err := exec.Command("sync").Run(); err != nil { return fmt.Errorf("cannot sync filesystems: %s", err) } diff --git a/vendor/vendor.json b/vendor/vendor.json index c383e5dd57f..3601025b528 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -24,6 +24,13 @@ "revision": "97646858c46433e4afb3432ad28c12e968efa298", "revisionTime": "2017-08-22T15:24:03Z" }, + { + "checksumSHA1": "M1jObMAw/YV+Rc5PccfmJuuxLa0=", + "origin": "github.com/chrisccoulson/go-tpm", + "path": "github.com/google/go-tpm", + "revision": "c6c7cb7465ae50e13263f2f8ff33f57d1f9859bc", + "revisionTime": "2019-07-08T13:36:22Z" + }, { "checksumSHA1": "iIUYZyoanCQQTUaWsu8b+iOSPt4=", "path": "github.com/gorilla/context", From f2ac2bfbdb90bd0a6cff49d8d9366eb1c3af4303 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 10 Jul 2019 09:41:15 +0200 Subject: [PATCH 2/9] vendor.json: add "tree": true --- vendor/vendor.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 3601025b528..f20f3a934bc 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -25,11 +25,12 @@ "revisionTime": "2017-08-22T15:24:03Z" }, { - "checksumSHA1": "M1jObMAw/YV+Rc5PccfmJuuxLa0=", + "checksumSHA1": "mkW1FNv2atdJblBoNsGkAfvec5w=", "origin": "github.com/chrisccoulson/go-tpm", "path": "github.com/google/go-tpm", "revision": "c6c7cb7465ae50e13263f2f8ff33f57d1f9859bc", - "revisionTime": "2019-07-08T13:36:22Z" + "revisionTime": "2019-07-08T13:36:22Z", + "tree": true }, { "checksumSHA1": "iIUYZyoanCQQTUaWsu8b+iOSPt4=", From 8f2825c99a99201d857e8d7179153d2c2aeb81e9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 10 Jul 2019 11:06:52 +0200 Subject: [PATCH 3/9] fdeutils: do not build on arm64 (no syscall.SYS_POLL there) --- recovery/fdeutils/provisioning.go | 2 ++ recovery/fdeutils/provisioning_arm64.go | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 recovery/fdeutils/provisioning_arm64.go diff --git a/recovery/fdeutils/provisioning.go b/recovery/fdeutils/provisioning.go index e2b62a8733a..ad8187c6205 100644 --- a/recovery/fdeutils/provisioning.go +++ b/recovery/fdeutils/provisioning.go @@ -1,3 +1,5 @@ +// +build !arm64 + package fdeutils import ( diff --git a/recovery/fdeutils/provisioning_arm64.go b/recovery/fdeutils/provisioning_arm64.go new file mode 100644 index 00000000000..6fa4743adca --- /dev/null +++ b/recovery/fdeutils/provisioning_arm64.go @@ -0,0 +1,13 @@ +package fdeutils + +import ( + "fmt" +) + +func ProvisionTPM(lockoutAuth []byte) error { + return fmt.Errorf("ProvisionTPM not implemented on arm64") +} + +func RequestTPMClearUsingPPI() error { + return fmt.Errorf("RequestTPMClearUsingPPI not implemented on arm64") +} From 316fb75170a2a32332e15480bc54c4b9363e0ee9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Wed, 10 Jul 2019 11:32:36 +0200 Subject: [PATCH 4/9] recovery: import fdeutils from chris instead of copying it --- recovery/fdeutils/provisioning.go | 102 ------------------------ recovery/fdeutils/provisioning_arm64.go | 13 --- recovery/recovery.go | 3 +- vendor/vendor.json | 6 ++ 4 files changed, 8 insertions(+), 116 deletions(-) delete mode 100644 recovery/fdeutils/provisioning.go delete mode 100644 recovery/fdeutils/provisioning_arm64.go diff --git a/recovery/fdeutils/provisioning.go b/recovery/fdeutils/provisioning.go deleted file mode 100644 index ad8187c6205..00000000000 --- a/recovery/fdeutils/provisioning.go +++ /dev/null @@ -1,102 +0,0 @@ -// +build !arm64 - -package fdeutils - -import ( - "errors" - "fmt" - "os" - - "github.com/google/go-tpm/tpm2" - "github.com/google/go-tpm/tpmutil" -) - -const ( - srkHandle tpmutil.Handle = 0x81000000 - - tpmPath string = "/dev/tpm0" - ppiPath string = "/sys/class/tpm/tpm0/ppi/request" - - permanentProps uint32 = 0x00000200 - lockoutAuthSet uint32 = 1 << 2 - disableClear uint32 = 1 << 8 - - clearPPIRequest string = "5" -) - -var ( - ErrClearRequiresPPI = errors.New("clearing requires the use of the Physical Presence Interface") - - srkTemplate = tpm2.Public{ - Type: tpm2.AlgRSA, - NameAlg: tpm2.AlgSHA256, - Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | - tpm2.FlagUserWithAuth | tpm2.FlagRestricted | tpm2.FlagDecrypt, - AuthPolicy: nil, - RSAParameters: &tpm2.RSAParams{ - Symmetric: &tpm2.SymScheme{ - Alg: tpm2.AlgAES, - KeyBits: 128, - Mode: tpm2.AlgCFB}, - KeyBits: 2048, - Exponent: 0, - ModulusRaw: make([]byte, 256)}} -) - -func ProvisionTPM(lockoutAuth []byte) error { - rw, err := tpm2.OpenTPM(tpmPath) - if err != nil { - return fmt.Errorf("failed to open TPM device: %v", err) - } - - c, _, err := tpm2.GetCapability(rw, tpm2.CapabilityTPMProperties, 1, permanentProps) - if err != nil { - return fmt.Errorf("failed to request permanent properties: %v", err) - } - - p := c[0].(tpm2.TaggedProperty).Value - if p&lockoutAuthSet > 0 || p&disableClear > 0 { - return ErrClearRequiresPPI - } - - if err := tpm2.Clear(rw, tpm2.HandleLockout, ""); err != nil { - return fmt.Errorf("failed to clear the TPM: %v", err) - } - - h, _, err := tpm2.CreatePrimary(rw, tpm2.HandleOwner, tpm2.PCRSelection{}, "", "", srkTemplate) - if err != nil { - return fmt.Errorf("failed to create storage root key: %v", err) - } - - if err := tpm2.EvictControl(rw, "", tpm2.HandleOwner, h, srkHandle); err != nil { - return fmt.Errorf("failed to make storage root key persistent: %v", err) - } - - if err := tpm2.SetDictionaryAttackParameters(rw, 32, 7200, 86400, ""); err != nil { - return fmt.Errorf("failed to configure DA parameters: %v", err) - } - - if err := tpm2.DisableOwnerClear(rw, ""); err != nil { - return fmt.Errorf("failed to disable owner clear: %v", err) - } - - if err := tpm2.HierarchyChangeAuth(rw, tpm2.HandleLockout, "", string(lockoutAuth)); err != nil { - return fmt.Errorf("failed to set the lockout hierarchy authorization value: %v", err) - } - - return nil -} - -func RequestTPMClearUsingPPI() error { - f, err := os.OpenFile(ppiPath, os.O_WRONLY, 0) - if err != nil { - return fmt.Errorf("failed to open request handle: %v", err) - } - defer f.Close() - - if _, err := f.WriteString(clearPPIRequest); err != nil { - return fmt.Errorf("failed to submit request: %v", err) - } - - return nil -} diff --git a/recovery/fdeutils/provisioning_arm64.go b/recovery/fdeutils/provisioning_arm64.go deleted file mode 100644 index 6fa4743adca..00000000000 --- a/recovery/fdeutils/provisioning_arm64.go +++ /dev/null @@ -1,13 +0,0 @@ -package fdeutils - -import ( - "fmt" -) - -func ProvisionTPM(lockoutAuth []byte) error { - return fmt.Errorf("ProvisionTPM not implemented on arm64") -} - -func RequestTPMClearUsingPPI() error { - return fmt.Errorf("RequestTPMClearUsingPPI not implemented on arm64") -} diff --git a/recovery/recovery.go b/recovery/recovery.go index 067a1989631..63837c2c0b5 100644 --- a/recovery/recovery.go +++ b/recovery/recovery.go @@ -29,11 +29,12 @@ import ( "path" "time" + "github.com/chrisccoulson/ubuntu-core-fde-utils" + "github.com/snapcore/snapd/bootloader" "github.com/snapcore/snapd/bootloader/grubenv" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/osutil" - "github.com/snapcore/snapd/recovery/fdeutils" ) const ( diff --git a/vendor/vendor.json b/vendor/vendor.json index f20f3a934bc..726726dd99f 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -6,6 +6,12 @@ "path": "context", "revision": "" }, + { + "checksumSHA1": "aqEmZAcJjaqq78RO9gS5HFI4x+4=", + "path": "github.com/chrisccoulson/ubuntu-core-fde-utils", + "revision": "aa681bc738a539828d498040e344fdad2514be7c", + "revisionTime": "2019-07-08T19:54:37Z" + }, { "checksumSHA1": "zg16zjZTQ9R89+UOLmEZxHgxDtM=", "path": "github.com/coreos/go-systemd/activation", From 08c31d593b4549dfecdd0a515dc6f500e89fa1d4 Mon Sep 17 00:00:00 2001 From: Claudio Matsuoka Date: Wed, 10 Jul 2019 13:32:40 -0300 Subject: [PATCH 5/9] recovery: pass key to cryptsetup using stdin Don't store the unsealed key to the encrypted partition in the filesystem, write it to cryptsetup's stdin instead. Signed-off-by: Claudio Matsuoka --- recovery/device.go | 76 +++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/recovery/device.go b/recovery/device.go index c95186fc6bf..522339ef56e 100644 --- a/recovery/device.go +++ b/recovery/device.go @@ -20,6 +20,7 @@ package recovery import ( "bufio" + "bytes" "fmt" "io/ioutil" "os" @@ -51,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 @@ -89,7 +77,7 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyBuffer [] 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) @@ -118,32 +106,22 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyBuffer [] // see https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1835279 time.Sleep(1 * time.Second) - // Create a temporary unsealed keyfile on a RAM-backed filesystem - keyfile := "/run/tmpkeyfile" - if err := ioutil.WriteFile(keyfile, keyBuffer, 0400); err != nil { - return fmt.Errorf("cannot create keyfile: %s", err) - } - + // 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)) } - // FIXME: use secure delete - if err := os.Remove(keyfile); err != nil { - logger.Noticef("can't remove keyfile: %s", err) - } - // 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 { @@ -161,6 +139,36 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyBuffer [] return nil } +func pipeRun(input []byte, name string, args ...string) (output []byte, err 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 + } + if err = cmd.Start(); err != nil { + return + } + n, err := stdin.Write(input) + if err != nil { + return + } + if n != len(input) { + err = fmt.Errorf("short write (%d)", n) + return + } + if err = stdin.Close(); err != nil { + return + } + if err = cmd.Wait(); err != nil { + output = out.Bytes() + return + } + return +} + 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 { From 6e4b60d7b1f83be361107b559ddc2c3a7c518e0d Mon Sep 17 00:00:00 2001 From: Claudio Matsuoka Date: Wed, 10 Jul 2019 14:16:09 -0300 Subject: [PATCH 6/9] recovery: use sync from package syscall Signed-off-by: Claudio Matsuoka --- recovery/recovery.go | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/recovery/recovery.go b/recovery/recovery.go index 63837c2c0b5..7cc29d4e61a 100644 --- a/recovery/recovery.go +++ b/recovery/recovery.go @@ -27,6 +27,7 @@ import ( "os" "os/exec" "path" + "syscall" "time" "github.com/chrisccoulson/ubuntu-core-fde-utils" @@ -205,9 +206,7 @@ func Install(version string) error { return fmt.Errorf("cannot store lockout authorization: %s", err) } - if err := exec.Command("sync").Run(); err != nil { - return fmt.Errorf("cannot sync: %s", err) - } + syscall.Sync() if err := umount(mntWritable); err != nil { return err @@ -231,7 +230,7 @@ func Install(version string) error { } func createWritable(keyBuffer []byte, cryptdev string) error { - logger.Noticef("Creating new ubuntu-data") + 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) @@ -267,9 +266,7 @@ func storeKeyfile(dir string, buffer []byte) error { } // Don't remove this sync, it prevents file corruption on vfat - if err := exec.Command("sync").Run(); err != nil { - return fmt.Errorf("cannot sync: %s", err) - } + syscall.Sync() return nil } @@ -380,9 +377,7 @@ func extractKernel(kernelPath, mntSystemBoot string) error { } // Don't remove this sync, it prevents file corruption on vfat - if err := exec.Command("sync").Run(); err != nil { - return fmt.Errorf("cannot sync filesystems: %s", err) - } + syscall.Sync() if err := umount(mntKernelSnap); err != nil { return err From 7e796c977bb554141ed0532ef774b5c332799cd2 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 11 Jul 2019 09:37:41 +0200 Subject: [PATCH 7/9] recovery: fix naked return error from travis --- recovery/device.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/recovery/device.go b/recovery/device.go index 522339ef56e..e105c391e56 100644 --- a/recovery/device.go +++ b/recovery/device.go @@ -139,34 +139,33 @@ func (d *DiskDevice) CreateLUKSPartition(size uint64, label string, keyBuffer [] return nil } -func pipeRun(input []byte, name string, args ...string) (output []byte, err error) { +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 + return nil, err } if err = cmd.Start(); err != nil { - return + return nil, err } n, err := stdin.Write(input) if err != nil { - return + return nil, err } if n != len(input) { err = fmt.Errorf("short write (%d)", n) - return + return nil, err } if err = stdin.Close(); err != nil { - return + return nil, err } if err = cmd.Wait(); err != nil { - output = out.Bytes() - return + return nil, err } - return + return out.Bytes(), nil } func createCrypttab(partdev, keyfile, cryptdev string) error { From 1d70445c82d643c515a7df61d15b9187e016042e Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 11 Jul 2019 12:01:48 +0200 Subject: [PATCH 8/9] vendor: update github.com/chrisccoulson/ubuntu-core-fde-utils to fix FTBFS on arm64 --- vendor/vendor.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 726726dd99f..f12ebc82afb 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -7,10 +7,10 @@ "revision": "" }, { - "checksumSHA1": "aqEmZAcJjaqq78RO9gS5HFI4x+4=", + "checksumSHA1": "X9uJjtPte7hdg0/LSEtnDD4v6Iw=", "path": "github.com/chrisccoulson/ubuntu-core-fde-utils", - "revision": "aa681bc738a539828d498040e344fdad2514be7c", - "revisionTime": "2019-07-08T19:54:37Z" + "revision": "49b2130cfa1a95f940eaf348b3a71b3f7af344ba", + "revisionTime": "2019-07-11T08:52:23Z" }, { "checksumSHA1": "zg16zjZTQ9R89+UOLmEZxHgxDtM=", From bc0c3dbda02234ee18f02859623334927ae535e8 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 11 Jul 2019 13:10:07 +0200 Subject: [PATCH 9/9] recovery: rename fdeutils->fdeutil (upstream changed this) --- recovery/recovery.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recovery/recovery.go b/recovery/recovery.go index 7cc29d4e61a..42dc65f4c58 100644 --- a/recovery/recovery.go +++ b/recovery/recovery.go @@ -193,7 +193,7 @@ func Install(version string) error { } logger.Noticef("Provisioning the TPM") - if err := fdeutils.ProvisionTPM(lockoutAuth); err != nil { + if err := fdeutil.ProvisionTPM(lockoutAuth); err != nil { logger.Noticef("error provisioning the TPM: %s", err) return fmt.Errorf("cannot provision TPM: %s", err) }