1 : /*
2 : * Copyright (c) 2013 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include "native_client/src/trusted/validator/validation_cache.h"
8 :
9 : #include <string.h>
10 : #include <sys/stat.h>
11 :
12 : #include "native_client/src/public/desc_metadata_types.h"
13 : #include "native_client/src/shared/platform/nacl_check.h"
14 : #include "native_client/src/shared/platform/nacl_host_desc.h"
15 : #include "native_client/src/trusted/desc/nacl_desc_base.h"
16 : #include "native_client/src/trusted/desc/nacl_desc_io.h"
17 : #include "native_client/src/trusted/service_runtime/include/sys/fcntl.h"
18 : #include "native_client/src/trusted/validator/rich_file_info.h"
19 : #include "native_client/src/trusted/validator/validation_cache_internal.h"
20 : #include "native_client/src/trusted/validator/validation_metadata.h"
21 :
22 : #if NACL_WINDOWS
23 : #include <Windows.h>
24 : #include <io.h>
25 : #endif
26 :
27 : #define ADD_LITERAL(cache, query, data) \
28 : ((cache)->AddData((query), (uint8_t*)&(data), sizeof(data)))
29 :
30 11 : int NaClCachingIsInexpensive(struct NaClValidationCache *cache,
31 : const struct NaClValidationMetadata *metadata) {
32 11 : if (cache->CachingIsInexpensive != NULL) {
33 11 : return cache->CachingIsInexpensive(metadata);
34 : } else {
35 0 : return NULL != metadata && metadata->identity_type == NaClCodeIdentityFile;
36 : }
37 : }
38 :
39 7 : void NaClMetadataFromFDCtor(struct NaClValidationMetadata *metadata,
40 : int file_desc,
41 : const char* file_name,
42 : size_t file_name_length) {
43 : struct NaClHostDesc wrapper;
44 : nacl_host_stat_t stat;
45 : #if NACL_WINDOWS
46 : BY_HANDLE_FILE_INFORMATION file_info;
47 : #endif
48 :
49 7 : memset(metadata, 0, sizeof(*metadata));
50 : /* If we early out, identity_type will be 0 / NaClCodeIdentityData. */
51 :
52 7 : wrapper.d = file_desc;
53 7 : if(NaClHostDescFstat(&wrapper, &stat))
54 7 : return;
55 :
56 : #if NACL_WINDOWS
57 : /*
58 : * This will not get us the complete file ID on ReFS, but doing the correct
59 : * thing (calling GetFileInformationByHandleEx) causes linkage issues on
60 : * Windows XP. We aren't relying on the file ID for security, just collision
61 : * resistance, so we don't need all of it.
62 : * In many cases (including on NTFS) we're also getting the 32 least
63 : * significant bits of a 64-bit volume serial number - but again, since it's
64 : * random we can live with it.
65 : */
66 : if (!GetFileInformationByHandle((HANDLE) _get_osfhandle(file_desc),
67 : &file_info))
68 : return;
69 : metadata->device_id = file_info.dwVolumeSerialNumber;
70 : metadata->file_id = ((((uint64_t)file_info.nFileIndexHigh) << 32) |
71 : file_info.nFileIndexLow);
72 : #else
73 : /* st_dev is not actually a property of the device, so skip it. */
74 7 : metadata->file_id = stat.st_ino;
75 : #endif
76 :
77 7 : metadata->file_size = stat.st_size;
78 7 : metadata->mtime = stat.st_mtime;
79 7 : metadata->ctime = stat.st_ctime;
80 :
81 7 : CHECK(0 < file_name_length);
82 7 : metadata->file_name = malloc(file_name_length);
83 7 : CHECK(NULL != metadata->file_name);
84 7 : memcpy(metadata->file_name, file_name, file_name_length);
85 7 : metadata->file_name_length = file_name_length;
86 :
87 : /* We have all the identity information we need. */
88 7 : metadata->identity_type = NaClCodeIdentityFile;
89 : }
90 :
91 12 : void NaClMetadataDtor(struct NaClValidationMetadata *metadata) {
92 12 : free(metadata->file_name);
93 : /* Prevent use after free. */
94 12 : memset(metadata, 0, sizeof(*metadata));
95 12 : }
96 :
97 66 : static void Serialize(uint8_t *buffer, const void *value, size_t size,
98 : uint32_t *offset) {
99 66 : if (buffer != NULL)
100 33 : memcpy(&buffer[*offset], value, size);
101 66 : *offset += (uint32_t) size;
102 66 : }
103 :
104 26 : static void SerializeNaClDescMetadataInternal(
105 : const struct NaClRichFileInfo *info,
106 : uint8_t *buffer,
107 : uint32_t *offset) {
108 26 : *offset = 0;
109 26 : Serialize(buffer, &info->known_file, sizeof(info->known_file), offset);
110 26 : if (info->known_file) {
111 20 : Serialize(buffer, &info->file_path_length, sizeof(info->file_path_length),
112 : offset);
113 20 : Serialize(buffer, info->file_path, info->file_path_length, offset);
114 : }
115 26 : }
116 :
117 13 : int NaClSerializeNaClDescMetadata(
118 : const struct NaClRichFileInfo *info,
119 : uint8_t **buffer,
120 : uint32_t *buffer_length) {
121 :
122 13 : *buffer = NULL;
123 :
124 : /* Calculate the buffer size. */
125 13 : SerializeNaClDescMetadataInternal(info, NULL, buffer_length);
126 :
127 : /* Allocate the buffer. */
128 13 : *buffer = malloc(*buffer_length);
129 13 : if (NULL == *buffer)
130 0 : return 1;
131 :
132 : /* Fill the buffer. */
133 13 : SerializeNaClDescMetadataInternal(info, *buffer, buffer_length);
134 13 : return 0;
135 : }
136 :
137 9 : int NaClSetFileOriginInfo(struct NaClDesc *desc,
138 : struct NaClRichFileInfo *info) {
139 9 : uint8_t *buffer = NULL;
140 9 : uint32_t buffer_length = 0;
141 : int status;
142 9 : if (NaClSerializeNaClDescMetadata(info, &buffer, &buffer_length)) {
143 0 : return 1;
144 : }
145 9 : status = NACL_VTBL(NaClDesc, desc)->SetMetadata(
146 : desc,
147 : NACL_DESC_METADATA_ORIGIN_INFO_TYPE,
148 : buffer_length,
149 : (uint8_t *) buffer);
150 9 : free(buffer);
151 9 : return status;
152 : }
153 :
154 29 : static int Deserialize(const uint8_t *buffer, uint32_t buffer_length,
155 : void *value, size_t size, uint32_t *offset) {
156 29 : if (*offset + size > buffer_length)
157 0 : return 1;
158 29 : memcpy(value, &buffer[*offset], size);
159 29 : *offset += (uint32_t) size;
160 29 : return 0;
161 : }
162 :
163 11 : int NaClDeserializeNaClDescMetadata(
164 : const uint8_t *buffer,
165 : uint32_t buffer_length,
166 : struct NaClRichFileInfo *info) {
167 : /* Work around const issues. */
168 11 : char *file_path = NULL;
169 11 : uint32_t offset = 0;
170 11 : NaClRichFileInfoCtor(info);
171 :
172 11 : if (Deserialize(buffer, buffer_length, &info->known_file,
173 : sizeof(info->known_file), &offset))
174 0 : goto on_error;
175 :
176 11 : if (info->known_file) {
177 9 : if (Deserialize(buffer, buffer_length, &info->file_path_length,
178 : sizeof(info->file_path_length), &offset))
179 0 : goto on_error;
180 9 : file_path = malloc(info->file_path_length);
181 9 : if (NULL == file_path)
182 0 : goto on_error;
183 9 : if (Deserialize(buffer, buffer_length, file_path, info->file_path_length,
184 : &offset))
185 0 : goto on_error;
186 9 : info->file_path = file_path;
187 9 : file_path = NULL;
188 : }
189 :
190 : /* Entire buffer consumed? */
191 11 : if (offset != buffer_length)
192 0 : goto on_error;
193 11 : return 0;
194 :
195 : on_error:
196 0 : free(file_path);
197 0 : NaClRichFileInfoDtor(info);
198 0 : return 1;
199 : }
200 :
201 44 : void NaClRichFileInfoCtor(struct NaClRichFileInfo *info) {
202 44 : memset(info, 0, sizeof(*info));
203 44 : }
204 :
205 21 : void NaClRichFileInfoDtor(struct NaClRichFileInfo *info) {
206 : /*
207 : * file_path is "const" to express intent, we need to cast away the const to
208 : * dallocate it.
209 : */
210 21 : free((void *) info->file_path);
211 : /* Prevent use after Dtor. */
212 21 : memset(info, 0, sizeof(*info));
213 21 : }
214 :
215 15 : int NaClGetFileOriginInfo(struct NaClDesc *desc,
216 : struct NaClRichFileInfo *info) {
217 : int32_t metadata_type;
218 15 : uint8_t *buffer = NULL;
219 15 : uint32_t buffer_length = 0;
220 : int status;
221 :
222 : /* Get the buffer length. */
223 15 : metadata_type = NACL_VTBL(NaClDesc, desc)->GetMetadata(
224 : desc,
225 : &buffer_length,
226 : NULL);
227 15 : if (metadata_type != NACL_DESC_METADATA_ORIGIN_INFO_TYPE)
228 6 : return 1;
229 :
230 9 : buffer = (uint8_t *) malloc(buffer_length);
231 9 : if (NULL == buffer)
232 0 : return 1;
233 :
234 9 : metadata_type = NACL_VTBL(NaClDesc, desc)->GetMetadata(
235 : desc,
236 : &buffer_length,
237 : buffer);
238 9 : if (metadata_type != NACL_DESC_METADATA_ORIGIN_INFO_TYPE)
239 0 : return 1;
240 :
241 9 : status = NaClDeserializeNaClDescMetadata(buffer, buffer_length, info);
242 9 : free(buffer);
243 9 : return status;
244 : }
245 :
246 12 : void NaClMetadataFromNaClDescCtor(struct NaClValidationMetadata *metadata,
247 : struct NaClDesc *desc) {
248 : struct NaClRichFileInfo info;
249 12 : int32_t fd = -1;
250 :
251 12 : NaClRichFileInfoCtor(&info);
252 12 : memset(metadata, 0, sizeof(*metadata));
253 :
254 12 : if (NACL_VTBL(NaClDesc, desc)->typeTag != NACL_DESC_HOST_IO)
255 0 : goto done;
256 12 : fd = ((struct NaClDescIoDesc *) desc)->hd->d;
257 12 : if (NaClGetFileOriginInfo(desc, &info))
258 5 : goto done;
259 7 : if (!info.known_file || info.file_path == NULL || info.file_path_length <= 0)
260 : goto done;
261 7 : NaClMetadataFromFDCtor(metadata, fd, info.file_path, info.file_path_length);
262 : done:
263 12 : NaClRichFileInfoDtor(&info);
264 12 : }
265 :
266 11 : void NaClAddCodeIdentity(uint8_t *data,
267 : size_t size,
268 : const struct NaClValidationMetadata *metadata,
269 : struct NaClValidationCache *cache,
270 : void *query) {
271 : NaClCodeIdentityType identity_type;
272 11 : if (NULL != metadata) {
273 5 : identity_type = metadata->identity_type;
274 : } else {
275 : /* Fallback: identity unknown, treat it as anonymous data. */
276 6 : identity_type = NaClCodeIdentityData;
277 : }
278 11 : CHECK(identity_type < NaClCodeIdentityMax);
279 :
280 : /*
281 : * Explicitly record the type of identity being used to prevent attacks
282 : * that confuse the payload of different identity types.
283 : */
284 11 : ADD_LITERAL(cache, query, identity_type);
285 :
286 11 : if (identity_type == NaClCodeIdentityFile) {
287 : /* Sanity checks. */
288 5 : CHECK(metadata->file_name);
289 5 : CHECK(metadata->file_name_length);
290 5 : CHECK(metadata->code_offset + (int64_t) size <= metadata->file_size);
291 :
292 : /* The location of the code in the file. */
293 5 : ADD_LITERAL(cache, query, metadata->code_offset);
294 5 : ADD_LITERAL(cache, query, size);
295 :
296 : /* The identity of the file. */
297 5 : ADD_LITERAL(cache, query, metadata->file_name_length);
298 5 : cache->AddData(query, (uint8_t *) metadata->file_name,
299 : metadata->file_name_length);
300 5 : ADD_LITERAL(cache, query, metadata->device_id);
301 5 : ADD_LITERAL(cache, query, metadata->file_id);
302 5 : ADD_LITERAL(cache, query, metadata->file_size);
303 5 : ADD_LITERAL(cache, query, metadata->mtime);
304 5 : ADD_LITERAL(cache, query, metadata->ctime);
305 : } else {
306 : /* Hash all the code. */
307 6 : cache->AddData(query, data, size);
308 : }
309 11 : }
310 :
311 0 : struct NaClDesc *NaClDescCreateWithFilePathMetadata(NaClHandle handle,
312 : const char *file_path) {
313 0 : struct NaClDesc *desc = NaClDescIoDescFromHandleAllocCtor(handle,
314 : NACL_ABI_O_RDONLY);
315 : char *alloc_file_path;
316 0 : size_t file_path_length = strlen(file_path);
317 :
318 : struct NaClRichFileInfo info;
319 :
320 0 : if (desc == NULL)
321 0 : return NULL;
322 :
323 : /*
324 : * If there is no file path metadata, just return the created NaClDesc
325 : * without adding rich file info.
326 : */
327 0 : if (file_path_length == 0)
328 0 : return desc;
329 :
330 : /* Mark the desc as OK for mmapping. */
331 0 : NaClDescMarkSafeForMmap(desc);
332 :
333 0 : alloc_file_path = (char *) malloc(file_path_length + 1);
334 0 : if (alloc_file_path == NULL) {
335 0 : NaClDescUnref(desc);
336 0 : return NULL;
337 : }
338 0 : memcpy(alloc_file_path, file_path, file_path_length + 1);
339 :
340 : /* Provide metadata for validation. */
341 0 : NaClRichFileInfoCtor(&info);
342 0 : info.known_file = 1;
343 0 : info.file_path = alloc_file_path; /* Takes ownership. */
344 0 : info.file_path_length = (uint32_t) file_path_length;
345 0 : NaClSetFileOriginInfo(desc, &info);
346 0 : NaClRichFileInfoDtor(&info);
347 0 : return desc;
348 : }
|