forked from Velocidex/velociraptor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfilebased_utils.go
217 lines (187 loc) · 5.74 KB
/
filebased_utils.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
package datastore
import (
"crypto/sha1"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
api_proto "www.velocidex.com/golang/velociraptor/api/proto"
config_proto "www.velocidex.com/golang/velociraptor/config/proto"
"www.velocidex.com/golang/velociraptor/file_store/api"
"www.velocidex.com/golang/velociraptor/file_store/path_specs"
"www.velocidex.com/golang/velociraptor/utils"
)
var (
sep = string(filepath.Separator)
)
func AsFilestoreFilename(
db DataStore, config_obj *config_proto.Config, path api.FSPathSpec) string {
return AsFilestoreDirectory(db, config_obj, path) +
api.GetExtensionForFilestore(path)
}
func AsFilestoreDirectory(
db DataStore, config_obj *config_proto.Config,
path api.FSPathSpec) string {
data_store_root := ""
if config_obj != nil && config_obj.Datastore != nil {
data_store_root = config_obj.Datastore.FilestoreDirectory
}
if path.IsSafe() {
return asSafeDirWithRoot(path.AsDatastorePath(), data_store_root)
}
return asUnsafeDirWithRoot(db, config_obj,
path.AsDatastorePath(), data_store_root)
}
func AsDatastoreDirectory(
db DataStore, config_obj *config_proto.Config, path api.DSPathSpec) string {
location := ""
if config_obj.Datastore != nil {
location = config_obj.Datastore.Location
}
if path.IsSafe() {
return asSafeDirWithRoot(path, location)
}
return asUnsafeDirWithRoot(db, config_obj, path, location)
}
// When we are unsafe we need to Sanitize components hitting the
// filesystem.
func asUnsafeDirWithRoot(
db DataStore,
config_obj *config_proto.Config,
path api.DSPathSpec, root string) string {
new_components := make([]string, 0, len(path.Components()))
for _, i := range path.Components() {
if i != "" {
new_components = append(new_components, CompressComponent(
db, config_obj, i))
}
}
result := ""
if len(new_components) > 0 {
result = sep + strings.Join(new_components, sep)
}
// This relies on the filepath starting with a drive letter
// and having \ as path separators. Main's
// validateServerConfig() ensures this is the case.
if runtime.GOOS == "windows" {
return "\\\\?\\" + root + result
}
return root + result
}
func AsDatastoreFilename(
db DataStore, config_obj *config_proto.Config, path api.DSPathSpec) string {
return AsDatastoreDirectory(db, config_obj, path) +
api.GetExtensionForDatastore(path)
}
// If the path spec is already safe we can shortcut it and not
// sanitize. Safe paths are assumed to be generated from within the
// application and therefore can not overflow path lengths - so we
// dont need to compress them.
func asSafeDirWithRoot(
path api.DSPathSpec, root string) string {
// No need to sanitize here because the DSPathSpec is already
// safe.
sep := string(filepath.Separator)
// This relies on the filepath starting with a drive letter
// and having \ as path separators, and having a trailing
// \. Main's config.ValidateDatastoreConfig() ensures this is
// the case.
if runtime.GOOS == "windows" {
// Remove empty components which are broken on windows due to
// the long filename hack.
components := make([]string, 0, len(path.Components()))
for _, c := range path.Components() {
if c != "" {
components = append(components, c)
}
}
return WINDOWS_LFN_PREFIX + root + sep +
strings.Join(components, sep)
}
return root + sep + strings.Join(path.Components(), sep)
}
// This function is only really called when listing a directory - we
// find the hash compressed member and need to reconstruct its full
// name.
func UncompressComponent(
db DataStore,
config_obj *config_proto.Config,
component string) string {
if len(component) == 0 || component[0] != '#' {
return utils.UnsanitizeComponent(component)
}
ds_pathspec := &api_proto.DSPathSpec{}
err := db.GetSubject(config_obj,
LFNCompressedHashPath(component), ds_pathspec)
if err != nil || len(ds_pathspec.Components) != 1 {
return component
}
return ds_pathspec.Components[0]
}
// Sanitize the component for the filesystem. If the component name is
// too long we also compress it by replacing it with a hash and
// storing the long path is a different area of the datastore. The
// result is that we can transparently write files with arbitrarily
// long paths, regardless of the capabilities of the underlying
// filesystem.
func CompressComponent(
db DataStore,
config_obj *config_proto.Config,
component string) string {
sanitized_component := utils.SanitizeString(component)
if len(sanitized_component) < 250 {
return sanitized_component
}
return compressComponent(db, config_obj, component)
}
func compressComponent(
db DataStore,
config_obj *config_proto.Config,
component string) string {
// Hash compress the original component
hash := sha1.Sum([]byte(component))
component_hash := fmt.Sprintf("#%x", hash)
db, err := GetDB(config_obj)
if err != nil {
return component_hash
}
ds_pathspec := &api_proto.DSPathSpec{
Components: []string{component},
}
_ = db.SetSubject(config_obj,
LFNCompressedHashPath(component_hash), ds_pathspec)
return component_hash
}
// Long file names are compressed into hashes and stored in the
// datastore.
func LFNCompressedHashPath(hash string) api.DSPathSpec {
res := path_specs.NewSafeDatastorePath("lfn_hashes").
SetType(api.PATH_TYPE_DATASTORE_JSON)
i := 0
var part []byte
for _, c := range []byte(hash) {
if c == '#' {
continue
}
part = append(part, c)
i++
if i == 3 || i == 12 {
res = res.AddChild(string(part))
part = nil
}
}
if len(part) > 0 {
res = res.AddChild(string(part))
}
return res
}
// Ensure all intermediate directories exist.
func MkdirAll(
db DataStore,
config_obj *config_proto.Config,
dirname api.FSPathSpec) error {
file_path := AsFilestoreFilename(db, config_obj,
dirname.SetType(api.PATH_TYPE_DATASTORE_DIRECTORY))
return os.MkdirAll(file_path, 0700)
}