Skip to content

Commit 620d0b7

Browse files
committed
fix(cab): accept cabinets with reserved padding
1 parent eff6861 commit 620d0b7

2 files changed

Lines changed: 68 additions & 30 deletions

File tree

lib/cabfile/cabfile.go

Lines changed: 64 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import (
2121
"crypto"
2222
"encoding/binary"
2323
"errors"
24+
"fmt"
2425
"hash"
2526
"io"
26-
"io/ioutil"
2727

2828
"github.com/sassoftware/relic/v7/lib/binpatch"
2929
)
@@ -33,7 +33,7 @@ func Digest(r io.Reader, hashFunc crypto.Hash) (*CabinetDigest, error) {
3333
var d hash.Hash
3434
var dw io.Writer
3535
if hashFunc == 0 {
36-
dw = ioutil.Discard
36+
dw = io.Discard
3737
} else {
3838
d = hashFunc.New()
3939
dw = d
@@ -45,47 +45,68 @@ func Digest(r io.Reader, hashFunc crypto.Hash) (*CabinetDigest, error) {
4545
if cab.Header.Magic != Magic {
4646
return nil, errors.New("not a cab file")
4747
}
48-
4948
outHeader := cab.Header
49+
var addOffset int
5050
if cab.Header.Flags&FlagReservePresent != 0 {
51+
// read reserve header
5152
if err := binary.Read(r, binary.LittleEndian, &cab.ReserveHeader); err != nil {
5253
return nil, err
5354
}
54-
if cab.ReserveHeader.HeaderSize > 0 {
55-
if cab.ReserveHeader.HeaderSize != 20 {
56-
return nil, errors.New("unknown reserved data")
55+
if cab.ReserveHeader.HeaderSize < signatureHeaderSize || cab.ReserveHeader.FolderSize != 0 || cab.ReserveHeader.DataSize != 0 {
56+
return nil, errors.New("unknown reserved data")
57+
}
58+
cab.SignatureHeader = new(SignatureHeader)
59+
if err := binary.Read(r, binary.LittleEndian, cab.SignatureHeader); err != nil {
60+
return nil, err
61+
}
62+
if padding := cab.ReserveHeader.HeaderSize - signatureHeaderSize; padding > 0 {
63+
// cabinet files may be created with space reserved; ensure in this case that it's all zeroed out
64+
if cab.SignatureHeader.CabinetSize != 0 {
65+
// must not have both padding and an actual signature header
66+
return nil, fmt.Errorf("reserve header for signature is %d bytes; expected %d bytes",
67+
cab.ReserveHeader.HeaderSize, signatureHeaderSize)
5768
}
58-
cab.SignatureHeader = new(SignatureHeader)
59-
if err := binary.Read(r, binary.LittleEndian, cab.SignatureHeader); err != nil {
69+
pbuf := make([]byte, padding)
70+
if _, err := io.ReadFull(r, pbuf); err != nil {
6071
return nil, err
6172
}
62-
if cab.Header.TotalSize != cab.SignatureHeader.CabinetSize {
63-
return nil, errors.New("mismatch between cabinet size and signature header size")
73+
for _, v := range pbuf {
74+
if v != 0 {
75+
return nil, errors.New("invalid padding in signature reserve header")
76+
}
6477
}
65-
} else {
66-
return nil, errors.New("unknown reserved data")
67-
}
68-
if cab.ReserveHeader.FolderSize != 0 || cab.ReserveHeader.DataSize != 0 {
69-
return nil, errors.New("unknown reserved data")
78+
// remove padding, since the actual signature goes at the end of the file
79+
addOffset -= int(padding)
80+
cab.SignatureHeader = nil
81+
} else if cab.Header.TotalSize != cab.SignatureHeader.CabinetSize {
82+
// signature header (if present) must agree with cabinet header
83+
return nil, fmt.Errorf("cabinet size is %d but signature header specifies %d bytes",
84+
cab.Header.TotalSize, cab.SignatureHeader.CabinetSize)
7085
}
86+
} else {
87+
// make space for a new reserve header
88+
addOffset += reserveHeaderSize + signatureHeaderSize
7189
}
7290
if cab.Header.Flags&(FlagPrevCabinet|FlagNextCabinet) != 0 {
7391
return nil, errors.New("multipart cab files are not supported")
7492
} else if cab.Header.Flags&^FlagReservePresent != 0 {
7593
return nil, errors.New("unsupported flags in cabinet file")
7694
}
77-
var addOffset uint32
78-
var outSigHeader SignatureHeader
79-
if cab.SignatureHeader == nil {
80-
// make a new header and increment all the file offsets to make room for it
81-
addOffset = 24
82-
outHeader.TotalSize += addOffset
83-
outHeader.Flags |= FlagReservePresent
84-
outHeader.OffsetFiles += addOffset
85-
outSigHeader.Unknown1 = 0x100000
86-
outSigHeader.CabinetSize = outHeader.TotalSize
87-
} else {
88-
outSigHeader = *cab.SignatureHeader
95+
// add space for signature header, or remove excess padding
96+
add32(&outHeader.TotalSize, addOffset)
97+
add32(&outHeader.OffsetFiles, addOffset)
98+
outHeader.Flags |= FlagReservePresent
99+
// construct signature header
100+
outReserveHeader := ReserveHeader{HeaderSize: signatureHeaderSize}
101+
outSigHeader := SignatureHeader{
102+
Unknown1: 0x100000,
103+
CabinetSize: outHeader.TotalSize,
104+
}
105+
if cab.SignatureHeader != nil {
106+
// preserve unknown fields from old signature header just in case they're important
107+
outSigHeader.Unknown1 = cab.SignatureHeader.Unknown1
108+
outSigHeader.Unknown2 = cab.SignatureHeader.Unknown2
109+
outSigHeader.Unknown3 = cab.SignatureHeader.Unknown3
89110
}
90111
// digest the header
91112
sb := sigBlob{
@@ -105,26 +126,30 @@ func Digest(r io.Reader, hashFunc crypto.Hash) (*CabinetDigest, error) {
105126
// save the updated header for writing out later
106127
patched := bytes.NewBuffer(make([]byte, 0, outHeader.OffsetFiles))
107128
_ = binary.Write(patched, binary.LittleEndian, outHeader)
108-
_ = binary.Write(patched, binary.LittleEndian, ReserveHeader{20, 0, 0})
129+
_ = binary.Write(patched, binary.LittleEndian, outReserveHeader)
109130
_ = binary.Write(patched, binary.LittleEndian, outSigHeader)
110131
w := io.MultiWriter(dw, patched)
132+
// add offset to folder headers
111133
var fh FolderHeader
112134
for i := 0; i < int(cab.Header.NumFolders); i++ {
113135
if err := binary.Read(r, binary.LittleEndian, &fh); err != nil {
114136
return nil, err
115137
}
116-
fh.Offset += addOffset
138+
add32(&fh.Offset, addOffset)
117139
_ = binary.Write(w, binary.LittleEndian, fh)
118140
}
141+
// digest file contents
119142
if _, err := io.CopyN(dw, r, int64(cab.Header.TotalSize-cab.Header.OffsetFiles)); err != nil {
120143
return nil, err
121144
}
122145
if cab.SignatureHeader != nil {
146+
// read old signature for verification purposes
123147
cab.Signature = make([]byte, cab.SignatureHeader.SignatureSize)
124148
if _, err := io.ReadFull(r, cab.Signature); err != nil {
125149
return nil, err
126150
}
127151
}
152+
// ensure there is nothing after the cabinet and signature
128153
if _, err := r.Read(make([]byte, 1)); err == nil {
129154
return nil, errors.New("trailing garbage after cabinet")
130155
} else if err != io.EOF {
@@ -134,7 +159,12 @@ func Digest(r io.Reader, hashFunc crypto.Hash) (*CabinetDigest, error) {
134159
if d != nil {
135160
imprint = d.Sum(nil)
136161
}
137-
return &CabinetDigest{cab, imprint, hashFunc, patched.Bytes()}, nil
162+
return &CabinetDigest{
163+
Cabinet: cab,
164+
Imprint: imprint,
165+
HashFunc: hashFunc,
166+
Patched: patched.Bytes(),
167+
}, nil
138168
}
139169

140170
// Parse the cabinet file header and return it
@@ -160,3 +190,7 @@ func (d *CabinetDigest) MakePatch(pkcs []byte) *binpatch.PatchSet {
160190
p.Add(int64(d.Cabinet.Header.TotalSize), int64(d.Cabinet.SignatureHeader.Size()), padded)
161191
return p
162192
}
193+
194+
func add32(offset *uint32, increment int) {
195+
*offset = uint32(int64(*offset) + int64(increment))
196+
}

lib/cabfile/structs.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ type Header struct {
4343
CabNumber uint16
4444
}
4545

46+
const reserveHeaderSize = 4
47+
4648
type ReserveHeader struct {
4749
HeaderSize uint16
4850
FolderSize uint8
4951
DataSize uint8
5052
}
5153

54+
const signatureHeaderSize = 20
55+
5256
type SignatureHeader struct {
5357
Unknown1 uint32
5458
CabinetSize uint32

0 commit comments

Comments
 (0)