Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AB#2432: Check for unsupported eclient import #172

Merged
merged 3 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Move bundle & sign related ELF operations to elf.go
  • Loading branch information
Nirusu committed Sep 22, 2022
commit beb115f57fa0b3bc75d3379dbb43e75b083aa271
129 changes: 129 additions & 0 deletions ego/cli/elf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright (c) Edgeless Systems GmbH.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

package cli

import (
"debug/elf"
"encoding/binary"
"errors"
"io"
"os"
)

func (c *Cli) embedConfigAsPayload(path string, jsonData []byte) error {
// Load ELF executable
f, err := c.fs.OpenFile(path, os.O_RDWR, 0)
if err != nil {
return err
}
defer f.Close()

// Check if a payload already exists
payloadSize, payloadOffset, oeInfoOffset, err := getPayloadInformation(f)
if err != nil {
return err
}

// If a payload already exists, truncate the file to remove it
if payloadSize > 0 {
fileStat, err := f.Stat()
if err != nil {
return err
}

// Check if payload is at expected location
expectedPayloadOffset := fileStat.Size() - int64(payloadSize)
if expectedPayloadOffset != payloadOffset {
return errors.New("expected payload location does not match real payload location, cannot safely truncate old payload")
}

err = f.Truncate(payloadOffset)
if err != nil {
return err
}
} else if (payloadSize == 0) != (payloadOffset == 0) {
return errors.New("payload information in header is inconsistent, cannot continue")
}

// Get current file size to determine offset
fileStat, err := f.Stat()
if err != nil {
return err
}
filesize := fileStat.Size()

// Write payload offset and size to .oeinfo header
if err := writeUint64At(f, uint64(filesize), oeInfoOffset+2048); err != nil {
return err
}
if err := writeUint64At(f, uint64(len(jsonData)), oeInfoOffset+2056); err != nil {
return err
}

// And finally, append the payload to the file
n, err := f.WriteAt(jsonData, filesize)
if err != nil {
return err
} else if n != len(jsonData) {
return errors.New("failed to embed enclave.json as metadata")
}

return nil
}

func getPayloadInformation(f io.ReaderAt) (uint64, int64, int64, error) {
// .oeinfo + 2056 contains the size of an embedded Edgeless RT data payload.
// If it is > 0, a payload already exists.

elfFile, err := elf.NewFile(f)
if err != nil {
return 0, 0, 0, err
}

oeInfo := elfFile.Section(".oeinfo")
if oeInfo == nil {
return 0, 0, 0, ErrNoOEInfo
}

payloadOffset, err := readUint64At(oeInfo, 2048)
if err != nil {
return 0, 0, 0, err
}
payloadSize, err := readUint64At(oeInfo, 2056)
if err != nil {
return 0, 0, 0, err
}

return payloadSize, int64(payloadOffset), int64(oeInfo.Offset), nil
}

func writeUint64At(w io.WriterAt, x uint64, off int64) error {
xByte := make([]byte, 8)
binary.LittleEndian.PutUint64(xByte, x)

n, err := w.WriteAt(xByte, off)
if err != nil {
return err
} else if n != 8 {
return errors.New("did not write expected number of bytes")
}

return nil
}

func readUint64At(r io.ReaderAt, off int64) (uint64, error) {
xByte := make([]byte, 8)

n, err := r.ReadAt(xByte, off)
if err != nil {
return 0, err
} else if n != 8 {
return 0, errors.New("did not read expected number of bytes")
}

return binary.LittleEndian.Uint64(xByte), nil
}
151 changes: 151 additions & 0 deletions ego/cli/elf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) Edgeless Systems GmbH.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

package cli

import (
"encoding/json"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"

"ego/config"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// create an unsigned EGo executable
var elfUnsigned = func() []byte {
const outFile = "hello"
const srcFile = outFile + ".go"

goroot, err := filepath.Abs(filepath.Join("..", "..", "_ertgo"))
if err != nil {
panic(err)
}

dir, err := ioutil.TempDir("", "")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)

// write minimal source file
const src = `package main;import _"time";func main(){}`
if err := ioutil.WriteFile(filepath.Join(dir, srcFile), []byte(src), 0o400); err != nil {
panic(err)
}

// compile
cmd := exec.Command(filepath.Join(goroot, "bin", "go"), "build", srcFile)
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GOROOT="+goroot)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}

// read resulting executable
data, err := ioutil.ReadFile(filepath.Join(dir, outFile))
if err != nil {
panic(err)
}

return data
}()

func TestEmbedConfigAsPayload(t *testing.T) {
require := require.New(t)
assert := assert.New(t)

// Setup test environment
fs := afero.Afero{Fs: afero.NewMemMapFs()}
runner := signRunner{fs: fs}
cli := NewCli(&runner, fs)

// Copy from hostfs to memfs
const exe = "helloworld"
require.NoError(fs.WriteFile(exe, elfUnsigned, 0))

// Check if no payload exists
unsignedExeMemfs, err := fs.Open(exe)
require.NoError(err)
unsignedExeMemfsStat, err := unsignedExeMemfs.Stat()
require.NoError(err)
unsignedExeMemfsSize := unsignedExeMemfsStat.Size()
payloadSize, payloadOffset, oeInfoOffset, err := getPayloadInformation(unsignedExeMemfs)
assert.NoError(err)
assert.Zero(payloadSize)
assert.Zero(payloadOffset)
assert.NotZero(oeInfoOffset)
unsignedExeMemfs.Close()

// Create a default config we want to check
testConf := &config.Config{
Exe: exe,
Key: defaultPrivKeyFilename,
Debug: true,
HeapSize: 512, //[MB]
ProductID: 1,
SecurityVersion: 1,
}

// Marshal config
jsonData, err := json.Marshal(testConf)
require.NoError(err)
expectedLengthOfPayload := len(jsonData)

// Embed json config to helloworld
err = cli.embedConfigAsPayload(exe, jsonData)
assert.NoError(err)

// Check if new helloworld_signed now contains signed data
signedExeMemfs, err := fs.Open(exe)
require.NoError(err)
defer signedExeMemfs.Close()

payloadSize, payloadOffset, oeInfoOffset, err = getPayloadInformation(signedExeMemfs)
assert.NoError(err)
assert.EqualValues(expectedLengthOfPayload, payloadSize)
assert.EqualValues(unsignedExeMemfsSize, payloadOffset)
assert.NotZero(oeInfoOffset)

// Reconstruct the JSON and check equality
reconstructedJSON := make([]byte, payloadSize)
n, err := signedExeMemfs.ReadAt(reconstructedJSON, payloadOffset)
require.NoError(err)
require.EqualValues(payloadSize, n)
assert.EqualValues(jsonData, reconstructedJSON)

// Now modify the config, redo everything and see if trucate worked fine and everything still lines up
testConf.HeapSize = 5120
jsonNewData, err := json.Marshal(testConf)
require.NoError(err)
expectedLengthOfNewPayload := len(jsonNewData)

// Re-sign the already signed executable
err = cli.embedConfigAsPayload(exe, jsonNewData)
assert.NoError(err)
payloadSize, payloadOffset, _, err = getPayloadInformation(signedExeMemfs)
require.NoError(err)
assert.EqualValues(expectedLengthOfNewPayload, payloadSize)
assert.EqualValues(unsignedExeMemfsSize, payloadOffset)

// Reconstruct the JSON and check if it not the old one, but the new one
reconstructedJSON = make([]byte, payloadSize)
n, err = signedExeMemfs.ReadAt(reconstructedJSON, payloadOffset)
require.NoError(err)
require.EqualValues(payloadSize, n)

// Finally, check if we got the new JSON config and not the old one
assert.NotEqualValues(jsonData, reconstructedJSON)
assert.EqualValues(jsonNewData, reconstructedJSON)
}
Loading