From c79a510b9ccd225984fdf063c44814028acde5f9 Mon Sep 17 00:00:00 2001 From: David Dworken Date: Mon, 25 Apr 2022 22:03:31 -0700 Subject: [PATCH] Maybe working xattr support for code signing --- backend/server/server.go | 4 ++- client/lib/lib.go | 72 +++++++++++++++++----------------------- client/lib/lib_test.go | 54 ------------------------------ go.mod | 2 +- go.sum | 2 ++ 5 files changed, 37 insertions(+), 97 deletions(-) diff --git a/backend/server/server.go b/backend/server/server.go index 1d3b1e6..b3261f1 100644 --- a/backend/server/server.go +++ b/backend/server/server.go @@ -247,7 +247,7 @@ func decrementVersionIfInvalid(initialVersion string) string { } func assertValidUpdate(updateInfo shared.UpdateInfo) error { - urls := []string{updateInfo.LinuxAmd64Url, updateInfo.LinuxAmd64AttestationUrl, updateInfo.DarwinAmd64Url, updateInfo.DarwinAmd64AttestationUrl, updateInfo.DarwinArm64Url, updateInfo.DarwinArm64AttestationUrl} + urls := []string{updateInfo.LinuxAmd64Url, updateInfo.LinuxAmd64AttestationUrl, updateInfo.DarwinAmd64Url, updateInfo.DarwinAmd64AttestationUrl, updateInfo.DarwinArm64Url, updateInfo.DarwinArm64AttestationUrl, updateInfo.DarwinAmd64Xattr, updateInfo.DarwinAmd64Xattr} for _, url := range urls { resp, err := http.Get(url) if err != nil { @@ -297,8 +297,10 @@ func buildUpdateInfo(version string) shared.UpdateInfo { LinuxAmd64AttestationUrl: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-linux-amd64/hishtory-linux-amd64.intoto.jsonl", version), DarwinAmd64Url: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-darwin-amd64/hishtory-darwin-amd64", version), DarwinAmd64AttestationUrl: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-darwin-amd64/hishtory-darwin-amd64.intoto.jsonl", version), + DarwinAmd64Xattr: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-xattr/hishtory-darwin-amd64-xattr.json", version), DarwinArm64Url: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-darwin-arm64/hishtory-darwin-arm64", version), DarwinArm64AttestationUrl: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-darwin-arm64/hishtory-darwin-arm64.intoto.jsonl", version), + DarwinArm64Xattr: fmt.Sprintf("https://github.com/ddworken/hishtory/releases/download/%s-xattr/hishtory-darwin-arm64-xattr.json", version), Version: version, } } diff --git a/client/lib/lib.go b/client/lib/lib.go index 815d42d..b1f5014 100644 --- a/client/lib/lib.go +++ b/client/lib/lib.go @@ -2,7 +2,6 @@ package lib import ( "bytes" - "encoding/hex" "encoding/json" "errors" "fmt" @@ -14,7 +13,6 @@ import ( "os/exec" "os/user" "path" - "regexp" "runtime" "strconv" "strings" @@ -24,6 +22,8 @@ import ( _ "embed" // for embedding config.sh + "golang.org/x/sys/unix" + "github.com/glebarez/sqlite" // an alternate non-cgo-requiring sqlite driver "gorm.io/gorm" "gorm.io/gorm/logger" @@ -719,52 +719,42 @@ func ApiPost(path, contentType string, data []byte) ([]byte, error) { return respBody, nil } -func parseXattr(xattrDump string) (map[string][]byte, error) { - m := make(map[string][]byte) - nextLineIsAttrName := true - attrName := "" - attrValue := make([]byte, 0) - for _, line := range strings.Split(xattrDump, "\n") { - if nextLineIsAttrName { - attrName = line[:len(line)-1] - nextLineIsAttrName = false - } else { - r := regexp.MustCompile("\\d{8} (?P([A-Z0-9]{2} )+)\\s+\\|[^\\s]+\\|") - match := r.FindStringSubmatch(line) - if match != nil { - for i, name := range r.SubexpNames() { - if name == "hex" { - bytes, err := hex.DecodeString(strings.ReplaceAll(match[i], " ", "")) - if err != nil { - return nil, fmt.Errorf("failed to decode hex string %#v in xattr file: %v", match[i], err) - } - attrValue = append(attrValue, bytes...) - } - } - } else { - if strings.Contains(line, "|") { - return nil, fmt.Errorf("entered confusing state in xattr file on line %#v, file=%#v", line, xattrDump) - } else { - nextLineIsAttrName = true - m[attrName] = attrValue - attrValue = make([]byte, 0) - } - } - } +type darwinCodeSignature struct { + Cd string `json:"cd"` + Cr string `json:"cr"` + Cr1 string `json:"cr1"` + Cs string `json:"cs"` +} + +func parseXattr(xattrDump string) (darwinCodeSignature, error) { + var xattr darwinCodeSignature + err := json.Unmarshal([]byte(xattrDump), &xattr) + if err != nil { + return xattr, fmt.Errorf("failed to parse xattr: %v", err) } - return m, nil + return xattr, nil } func setXattr(filename, xattrDump string) { - m, err := parseXattr(xattrDump) + x, err := parseXattr(xattrDump) if err != nil { panic(fmt.Errorf("failed to parse xattr file: %v", err)) } - for k, v := range m { - err := syscall.Setxattr(filename, k, v, 0) - if err != nil { - panic(fmt.Errorf("failed to set xattr %#v on file %#v: %v", k, filename, err)) - } + err = unix.Setxattr(filename, "com.apple.cs.CodeDirectory", []byte(x.Cd), 0) + if err != nil { + panic(fmt.Errorf("failed to set xattr com.apple.cs.CodeDirectory on file %#v: %v", filename, err)) + } + err = unix.Setxattr(filename, "com.apple.cs.CodeRequirements", []byte(x.Cr), 0) + if err != nil { + panic(fmt.Errorf("failed to set xattr com.apple.cs.CodeRequirements on file %#v: %v", filename, err)) + } + err = unix.Setxattr(filename, "com.apple.cs.CodeRequirements-1", []byte(x.Cr1), 0) + if err != nil { + panic(fmt.Errorf("failed to set xattr com.apple.cs.CodeRequirements-1 on file %#v: %v", filename, err)) + } + err = unix.Setxattr(filename, "com.apple.cs.CodeSignature", []byte(x.Cs), 0) + if err != nil { + panic(fmt.Errorf("failed to set xattr com.apple.cs.CodeSignature on file %#v: %v", filename, err)) } } diff --git a/client/lib/lib_test.go b/client/lib/lib_test.go index 65dff25..ddfba98 100644 --- a/client/lib/lib_test.go +++ b/client/lib/lib_test.go @@ -190,57 +190,3 @@ func TestParseCrossPlatformInt(t *testing.T) { t.Fatalf("failed to parse cross platform int %d", res) } } - -func TestParseXattr(t *testing.T) { - dump := `com.apple.macl: -00000000 04 00 34 5A 0D 8F 9B 10 48 FB 9D 12 E2 11 C7 21 |................| -00000010 D3 17 04 00 7D 17 C7 D7 51 B6 4B C4 B0 E5 1A 58 |................| -00000020 21 53 DD 4C 00 00 00 00 00 00 00 00 00 00 00 00 |!S.L............| -00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000040 00 00 00 00 00 00 00 00 |........�......| -00000048 -com.apple.metadata:kMDItemDownloadedDate: -00000000 62 70 6C 69 73 74 30 30 A1 01 33 41 C4 07 D4 F5 |bplist00..3A....| -00000010 D0 E7 A3 08 0A 00 00 00 00 00 00 01 01 00 00 00 |................| -00000020 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 |................| -00000030 00 00 00 00 13 |.....| -00000035 -com.apple.metadata:kMDItemWhereFroms: -00000000 62 70 6C 69 73 74 30 30 A2 01 02 5F 10 47 68 74 |bplist00..._.Ght| -00000010 74 70 73 3A 2F 2F 64 6C 2E 67 6F 6F 67 6C 65 2E |tps://dl.google.| -00000020 63 6F 6D 2F 63 68 72 6F 6D 65 2F 6D 61 63 2F 75 |com/chrome/mac/u| -00000030 6E 69 76 65 72 73 61 6C 2F 73 74 61 62 6C 65 2F |niversal/stable/| -00000040 47 47 52 4F 2F 67 6F 6F 67 6C 65 63 68 72 6F 6D |GGRO/googlechrom| -00000050 65 2E 64 6D 67 5F 10 17 68 74 74 70 73 3A 2F 2F |e.dmg_..https://| -00000060 77 77 77 2E 67 6F 6F 67 6C 65 2E 63 6F 6D 2F 08 |www.google.com/.| -00000092 -com.apple.quarantine: -00000000 30 31 38 33 3B 36 32 35 66 37 32 36 62 3B 53 61 |0183;625f726b;Sa| -00000010 66 61 72 69 3B 46 37 33 37 42 42 43 33 2D 30 41 |fari;F737BBC3-0A| -00000020 35 38 2D 34 31 44 34 2D 38 46 33 36 2D 30 33 42 |58-41D4-8F36-03B| -00000030 42 33 31 36 36 39 35 39 39 |B31669599| -00000039` - xattr, err := parseXattr(dump) - if err != nil { - t.Fatal(err) - } - if len(xattr) != 4 { - t.Fatalf("xattr has an incorrect length: %d", len(xattr)) - } - val := xattr["com.apple.quarantine"] - if string(val) != "0183;625f726b;Safari;F737BBC3-0A58-41D4-8F36-03BB31669599" { - t.Fatalf("unexpected xattr value=%#v", string(val)) - } - val = xattr["com.apple.metadata:kMDItemWhereFroms"] - if string(val) != "bplist00\xa2\x01\x02_\x10Ghttps://dl.google.com/chrome/mac/universal/stable/GGRO/googlechrome.dmg_\x10\x17https://www.google.com/\b" { - t.Fatalf("unexpected xattr value=%#v", string(val)) - } - val = xattr["com.apple.metadata:kMDItemDownloadedDate"] - if string(val) != "bplist00\xa1\x013A\xc4\a\xd4\xf5\xd0\xe7\xa3\b\n\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13" { - t.Fatalf("unexpected xattr value=%#v", string(val)) - } - val = xattr["com.apple.macl"] - if string(val) != "\x04\x004Z\r\x8f\x9b\x10H\xfb\x9d\x12\xe2\x11\xc7!\xd3\x17\x04\x00}\x17\xc7\xd7Q\xb6Kİ\xe5\x1aX!S\xddL\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" { - t.Fatalf("unexpected xattr value=%#v", string(val)) - } -} diff --git a/go.mod b/go.mod index 34bdf80..fb81ccc 100644 --- a/go.mod +++ b/go.mod @@ -206,7 +206,7 @@ require ( golang.org/x/net v0.0.0-20220325170049-de3da57026de // indirect golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect + golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect diff --git a/go.sum b/go.sum index 052541a..89b0e26 100644 --- a/go.sum +++ b/go.sum @@ -2573,6 +2573,8 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=