/
archive.go
138 lines (132 loc) · 3.4 KB
/
archive.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package main
import (
"archive/tar"
"archive/zip"
"github.com/nwaples/rardecode"
"io"
"io/ioutil"
"path/filepath"
"strings"
)
/* AllowPayload runs the appropriate decompress
function according to provided extension */
func AllowPayload(r *strings.Reader) (err error) {
// define list of payload functions to try
PayloadFuncList := []func(*strings.Reader)error{
AllowTarPayload,
AllowZipPayload,
AllowRarPayload,
}
for _, Payload := range PayloadFuncList {
// seek at the beginning of the stream and try next payload
if _, err = r.Seek(0, 0); err != nil {
return err
}
// check if payload was recognised and found to be clean
if err = Payload(r); err == nil {
break
}
// check if payload was recognised and blocked
if err == EPayloadNotAllowed {
return err
}
}
return nil
}
/* AllowTarPayload inspects a tar attachment in email message and
returns true if no filenames have a blacklisted extension */
func AllowTarPayload(r *strings.Reader) error {
// range over tar files
reader := tar.NewReader(r)
for {
// get next file in archive
header, err := reader.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
// check for blacklisted file name
FileExt := filepath.Ext(strings.ToLower(header.Name))
if !AllowFilename(FileExt) {
return EPayloadNotAllowed
}
// check for nested archives
slurp, err := ioutil.ReadAll(reader)
if err != nil {
// silently ignore errors
continue
}
// check if sub-payload contains any blacklisted files
if err := AllowPayload(strings.NewReader(string(slurp))); err != nil {
// error, return immediately
return err
}
}
return nil
}
/* AllowZipPayload inspects a zip attachment in email message and
returns true if no filenames have a blacklisted extension */
func AllowZipPayload(r *strings.Reader) error {
reader, err := zip.NewReader(r, int64(r.Len()))
if err != nil {
return err
}
// range over filenames in zip archive
for _, f := range reader.File {
FileExt := filepath.Ext(strings.ToLower(f.Name))
if !AllowFilename(FileExt) {
return EPayloadNotAllowed
}
// check archive within another achive
payload, err := f.Open()
if err != nil {
// silently ignore errors
continue
}
// read sub-payload
slurp, err := ioutil.ReadAll(payload)
// check if sub-payload contains any blacklisted files
if err := AllowPayload(strings.NewReader(string(slurp))); err != nil {
// error, return immediately
return err
}
}
return nil
}
/* AllowRarPayload inspects a rar attachment in email message and
returns true if no filenames have a blacklisted extension */
func AllowRarPayload(r *strings.Reader) error {
// make rar file reader object
rr, err := rardecode.NewReader(r, "")
if err != nil {
return err
}
// walk files in archive
for {
header, err := rr.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}
// compare current name against blacklisted extensions
FileExt := filepath.Ext(strings.ToLower(header.Name))
if !AllowFilename(FileExt) {
return EPayloadNotAllowed
}
// check archive within another achive
slurp, err := ioutil.ReadAll(rr)
if err != nil {
// silently ignore errors
continue
}
// check if sub-payload contains any blacklisted files
if err := AllowPayload(strings.NewReader(string(slurp))); err != nil {
// error, return immediately
return err
}
}
// no blacklisted file, allow
return nil
}