@@ -11,11 +11,12 @@ import (
11
11
12
12
"github.com/go-restruct/restruct"
13
13
14
+ "github.com/anchore/quill/internal/log"
14
15
"github.com/anchore/quill/quill/macho"
15
16
)
16
17
17
- func generateCodeDirectory (id string , hasher hash.Hash , m * macho.File , flags macho.CdFlag , requirementsHashBytes , entitlementsHashBytes [] byte ) (* macho.Blob , error ) {
18
- cd , err := newCodeDirectoryFromMacho (id , hasher , m , flags , requirementsHashBytes , entitlementsHashBytes )
18
+ func generateCodeDirectory (id string , hasher hash.Hash , m * macho.File , flags macho.CdFlag , specialSlots [] SpecialSlot ) (* macho.Blob , error ) {
19
+ cd , err := newCodeDirectoryFromMacho (id , hasher , m , flags , specialSlots )
19
20
if err != nil {
20
21
return nil , err
21
22
}
@@ -38,7 +39,7 @@ func packCodeDirectory(cd *macho.CodeDirectory, order binary.ByteOrder) (*macho.
38
39
return & blob , nil
39
40
}
40
41
41
- func newCodeDirectoryFromMacho (id string , hasher hash.Hash , m * macho.File , flags macho.CdFlag , requirementsHashBytes , entitlementsHashBytes [] byte ) (* macho.CodeDirectory , error ) {
42
+ func newCodeDirectoryFromMacho (id string , hasher hash.Hash , m * macho.File , flags macho.CdFlag , specialSlots [] SpecialSlot ) (* macho.CodeDirectory , error ) {
42
43
textSeg := m .Segment ("__TEXT" )
43
44
44
45
var codeSize uint32
@@ -58,14 +59,83 @@ func newCodeDirectoryFromMacho(id string, hasher hash.Hash, m *macho.File, flags
58
59
return nil , err
59
60
}
60
61
61
- return newCodeDirectory (id , hasher , textSeg .Offset , textSeg .Filesz , codeSize , hashes , flags , requirementsHashBytes , entitlementsHashBytes )
62
+ return newCodeDirectory (id , hasher , textSeg .Offset , textSeg .Filesz , codeSize , hashes , flags , specialSlots )
62
63
}
63
64
64
- func newCodeDirectory (id string , hasher hash.Hash , execOffset , execSize uint64 , codeSize uint32 , hashes [][]byte , flags macho.CdFlag , requirementsHashBytes , entitlementsHashBytes []byte ) (* macho.CodeDirectory , error ) {
65
+ // SpecialSlotHashWriter writes the special slots in the right order and with the right content.
66
+ // Special slots have a type defined in quill/macho/blob_index.go, their hashes must be written from higher
67
+ // type to lower type.
68
+ // All slot types between CsSlotInfoslot (1) and the higher valued type must be written to the file.
69
+ // The hashes for the missing slots must be filled with 0s.
70
+ //
71
+ // newCodeDirectory() also needs to know how many slots are present (including
72
+ // the 0-filled ones), and the total number of bytes which were written (to
73
+ // maintain an offset). It can use maxSlotType and totalBytesWritten for this.
74
+ type SpecialSlotHashWriter struct {
75
+ totalBytesWritten int // total number of bytes written by the Write method
76
+ // slot type with the higher int value. This corresponds to the number of slots which will be written
77
+ maxSlotType int
78
+ // used to detect inconsistencies in the provided hashes - they must all have the same size
79
+ hashSize int
80
+ // SpecialSlot map keyed by their type to easily detect which slot types are missing
81
+ slots map [int ]SpecialSlot
82
+ }
83
+
84
+ // newSpecialSlotHashWriter creates a new SpecialSlotHashWriter for the slots defined in specialSlots.
85
+ func newSpecialSlotHashWriter (specialSlots []SpecialSlot ) (* SpecialSlotHashWriter , error ) {
86
+ w := SpecialSlotHashWriter {}
87
+ w .slots = map [int ]SpecialSlot {}
88
+
89
+ for _ , slot := range specialSlots {
90
+ switch w .hashSize {
91
+ case 0 :
92
+ w .hashSize = len (slot .HashBytes )
93
+ case len (slot .HashBytes ):
94
+ // w.hashSize was set previously and has the right value, nothing to do
95
+ default :
96
+ return nil , fmt .Errorf ("inconsistent hash size: %d != %d" , w .hashSize , len (slot .HashBytes ))
97
+ }
98
+
99
+ slotType := int (slot .Type )
100
+ if slotType > w .maxSlotType {
101
+ w .maxSlotType = slotType
102
+ }
103
+ w .slots [slotType ] = slot
104
+ }
105
+
106
+ log .Debugf ("SpecialSlotHashWriter: %d special slots" , w .maxSlotType )
107
+
108
+ return & w , nil
109
+ }
110
+
111
+ // Write will write all the special slots hashes to w.buffer.
112
+ func (w * SpecialSlotHashWriter ) Write (buffer * bytes.Buffer ) error {
113
+ nullHashBytes := bytes .Repeat ([]byte {0 }, w .hashSize )
114
+ w .totalBytesWritten = 0
115
+
116
+ for i := w .maxSlotType ; i > 0 ; i -- {
117
+ log .Debugf ("SpecialSlotHashWriter: writing slot %d" , i )
118
+ hashBytes := nullHashBytes
119
+ slot , hasSlot := w .slots [i ]
120
+ if hasSlot {
121
+ hashBytes = slot .HashBytes
122
+ } else {
123
+ log .Debugf ("SpecialSlotHashWriter: slot %d is empty" , i )
124
+ }
125
+ written , err := buffer .Write (hashBytes )
126
+ if err != nil {
127
+ return fmt .Errorf ("unable to write plist hash to code directory: %w" , err )
128
+ }
129
+ w .totalBytesWritten += written
130
+ }
131
+
132
+ return nil
133
+ }
134
+
135
+ func newCodeDirectory (id string , hasher hash.Hash , execOffset , execSize uint64 , codeSize uint32 , hashes [][]byte , flags macho.CdFlag , specialSlots []SpecialSlot ) (* macho.CodeDirectory , error ) {
65
136
cdSize := unsafe .Sizeof (macho.BlobHeader {}) + unsafe .Sizeof (macho.CodeDirectoryHeader {})
66
137
idOff := int32 (cdSize )
67
138
// note: the hash offset starts at the first non-special hash (page hashes). Special hashes (e.g. requirements hash) are written before the page hashes.
68
- hashOff := idOff + int32 (len (id )+ 1 ) + int32 (len (requirementsHashBytes )) + int32 (len (entitlementsHashBytes ))
69
139
70
140
var ht macho.HashType
71
141
switch hasher .Size () {
@@ -80,17 +150,25 @@ func newCodeDirectory(id string, hasher hash.Hash, execOffset, execSize uint64,
80
150
buff := bytes.Buffer {}
81
151
82
152
// write the identifier
83
- if _ , err := buff .Write ([]byte (id + "\000 " )); err != nil {
153
+ hashOff := int (idOff )
154
+ var (
155
+ written int
156
+ err error
157
+ )
158
+ if written , err = buff .Write ([]byte (id + "\000 " )); err != nil {
84
159
return nil , fmt .Errorf ("unable to write ID to code directory: %w" , err )
85
160
}
161
+ hashOff += written
86
162
87
163
// write hashes
88
- if _ , err := buff .Write (requirementsHashBytes ); err != nil {
89
- return nil , fmt .Errorf ("unable to write requirements hash to code directory: %w" , err )
164
+ specialSlotHashWriter , err := newSpecialSlotHashWriter (specialSlots )
165
+ if err != nil {
166
+ return nil , err
90
167
}
91
- if _ , err := buff .Write (entitlementsHashBytes ); err != nil {
92
- return nil , fmt . Errorf ( "unable to write plist hash to code directory: %w" , err )
168
+ if err := specialSlotHashWriter .Write (& buff ); err != nil {
169
+ return nil , err
93
170
}
171
+ hashOff += specialSlotHashWriter .totalBytesWritten
94
172
95
173
for idx , hBytes := range hashes {
96
174
_ , err := buff .Write (hBytes )
@@ -105,7 +183,7 @@ func newCodeDirectory(id string, hasher hash.Hash, execOffset, execSize uint64,
105
183
Flags : flags ,
106
184
HashOffset : uint32 (hashOff ),
107
185
IdentOffset : uint32 (idOff ),
108
- NSpecialSlots : uint32 (2 ), // requirements + plist
186
+ NSpecialSlots : uint32 (specialSlotHashWriter . maxSlotType ),
109
187
NCodeSlots : uint32 (len (hashes )),
110
188
CodeLimit : codeSize ,
111
189
HashSize : uint8 (hasher .Size ()),
0 commit comments