Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
BinaryCookieReader.java
Go to the documentation of this file.
1/*
2 *
3 * Autopsy Forensic Browser
4 *
5 * Copyright 2019 Basis Technology Corp.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19package org.sleuthkit.autopsy.recentactivity;
20
21import java.io.DataInputStream;
22import java.io.File;
23import java.io.FileInputStream;
24import java.io.FileNotFoundException;
25import java.io.IOException;
26import java.nio.ByteBuffer;
27import java.nio.ByteOrder;
28import java.util.Iterator;
29import java.util.NoSuchElementException;
30import java.util.logging.Level;
31import org.sleuthkit.autopsy.coreutils.Logger;
32import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie;
33
43final class BinaryCookieReader implements Iterable<Cookie> {
44
45 private static final Logger LOG = Logger.getLogger(BinaryCookieReader.class.getName());
46 private static final int MAGIC_SIZE = 4;
47 private static final int SIZEOF_INT_BYTES = 4;
48 private static final int PAGE_HEADER_VALUE = 256;
49
50 private static final String COOKIE_MAGIC = "cook"; //NON-NLS
51
52 private static final int MAC_EPOC_FIX = 978307200;
53
54 private final int[] pageSizeArray;
55 private final File cookieFile;
56
62 private BinaryCookieReader(File cookieFile, int[] pageSizeArray) {
63 this.cookieFile = cookieFile;
64 this.pageSizeArray = pageSizeArray.clone();
65 }
66
79 public static BinaryCookieReader initalizeReader(File cookieFile) throws FileNotFoundException, IOException {
80 BinaryCookieReader reader = null;
81 try (DataInputStream dataStream = new DataInputStream(new FileInputStream(cookieFile))) {
82
83 byte[] magic = new byte[MAGIC_SIZE];
84 if (dataStream.read(magic) != MAGIC_SIZE) {
85 throw new IOException("Failed to read header, invalid file size (" + cookieFile.getName() + ")"); //NON-NLS
86 }
87
88 if (!(new String(magic)).equals(COOKIE_MAGIC)) {
89 throw new IOException(cookieFile.getName() + " is not a cookie file"); //NON-NLS
90 }
91
92 int[] sizeArray;
93 int pageCount = dataStream.readInt();
94 if (pageCount != 0) {
95 sizeArray = new int[pageCount];
96
97 for (int cnt = 0; cnt < pageCount; cnt++) {
98 sizeArray[cnt] = dataStream.readInt();
99 }
100 } else {
101 LOG.log(Level.INFO, "No cookies found in {0}", cookieFile.getName()); //NON-NLS
102 sizeArray = new int[0];
103 }
104
105 reader = new BinaryCookieReader(cookieFile, sizeArray);
106 }
107
108 return reader;
109 }
110
116 @Override
117 public Iterator<Cookie> iterator() {
118 return new CookiePageIterator();
119 }
120
124 private class CookiePageIterator implements Iterator<Cookie> {
125
126 int pageIndex = 0;
127 CookiePage currentPage = null;
128 Iterator<Cookie> currentIterator = null;
129 DataInputStream dataStream = null;
130
134 CookiePageIterator() {
135 if (pageSizeArray == null || pageSizeArray.length == 0) {
136 return;
137 }
138
139 try {
140 dataStream = new DataInputStream(new FileInputStream(cookieFile));
141 // skip to the first page
142 dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
143 } catch (IOException ex) {
144
145 String errorMessage = String.format("An error occurred creating an input stream for %s", cookieFile.getName());
146 LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
147 closeStream(); // Just incase the error was from skip
148 }
149 }
150
156 @Override
157 public boolean hasNext() {
158
159 if (dataStream == null) {
160 return false;
161 }
162
163 if (currentIterator == null || !currentIterator.hasNext()) {
164 try {
165
166 if (pageIndex < pageSizeArray.length) {
167 byte[] nextPage = new byte[pageSizeArray[pageIndex]];
168 dataStream.read(nextPage);
169
170 currentPage = new CookiePage(nextPage);
171 currentIterator = currentPage.iterator();
172 } else {
173 closeStream();
174 return false;
175 }
176
177 pageIndex++;
178 } catch (IOException ex) {
179 closeStream();
180 String errorMessage = String.format("A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
181 LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
182 return false;
183 }
184 }
185
186 return currentIterator.hasNext();
187 }
188
194 @Override
195 public Cookie next() {
196 // Just in case someone uses next without hasNext, this check will
197 // make sure there are more elements and that we iterate properly
198 // through the pages.
199 if (!hasNext()) {
200 throw new NoSuchElementException();
201 }
202 return currentIterator.next();
203 }
204
208 private void closeStream() {
209 if (dataStream != null) {
210 try {
211 dataStream.close();
212 dataStream = null;
213 } catch (IOException ex) {
214 String errorMessage = String.format("An error occurred trying to close stream for file %s", cookieFile.getName());
215 LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
216 }
217 }
218 }
219 }
220
224 private class CookiePage implements Iterable<Cookie> {
225
226 int[] cookieOffSets;
227 ByteBuffer pageBuffer;
228
237 CookiePage(byte[] page) throws IOException {
238 if (page == null || page.length == 0) {
239 throw new IllegalArgumentException("Invalid value for page passed to CookiePage constructor"); //NON-NLS
240 }
241
242 pageBuffer = ByteBuffer.wrap(page);
243
244 if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
245 pageBuffer = null;
246 throw new IOException("Invalid file format, bad page head value found"); //NON-NLS
247 }
248
249 pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
250 int count = pageBuffer.getInt();
251 cookieOffSets = new int[count];
252
253 for (int cnt = 0; cnt < count; cnt++) {
254 cookieOffSets[cnt] = pageBuffer.getInt();
255 }
256
257 pageBuffer.getInt(); // All 0, not needed
258 }
259
265 @Override
266 public Iterator<Cookie> iterator() {
267 return new CookieIterator();
268 }
269
273 private class CookieIterator implements Iterator<Cookie> {
274
275 int index = 0;
276
282 @Override
283 public boolean hasNext() {
284 if (pageBuffer == null) {
285 return false;
286 }
287
288 return index < cookieOffSets.length;
289 }
290
296 @Override
297 public Cookie next() {
298 if (!hasNext()) {
299 throw new NoSuchElementException();
300 }
301
302 int offset = cookieOffSets[index];
303 int size = pageBuffer.getInt(offset);
304 byte[] cookieBytes = new byte[size];
305 pageBuffer.get(cookieBytes, 0, size);
306 index++;
307
308 return new Cookie(cookieBytes);
309 }
310 }
311 }
312
316 public class Cookie {
317
318 private final static int COOKIE_HEAD_SKIP = 16;
319
320 private final double expirationDate;
321 private final double creationDate;
322
323 private final String name;
324 private final String url;
325 private final String path;
326 private final String value;
327
333 protected Cookie(byte[] cookieBytes) {
334 if (cookieBytes == null || cookieBytes.length == 0) {
335 throw new IllegalArgumentException("Invalid value for cookieBytes passed to Cookie constructor"); //NON-NLS
336 }
337
338 ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
339 byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
340
341 // Skip past the four int values that we are not interested in
342 byteBuffer.position(byteBuffer.position() + COOKIE_HEAD_SKIP);
343
344 int urlOffset = byteBuffer.getInt();
345 int nameOffset = byteBuffer.getInt();
346 int pathOffset = byteBuffer.getInt();
347 int valueOffset = byteBuffer.getInt();
348 byteBuffer.getLong(); // 8 bytes of not needed
349
350 expirationDate = byteBuffer.getDouble();
351 creationDate = byteBuffer.getDouble();
352
353 url = decodeString(cookieBytes, urlOffset);
354 name = decodeString(cookieBytes, nameOffset);
355 path = decodeString(cookieBytes, pathOffset);
356 value = decodeString(cookieBytes, valueOffset);
357 }
358
365 public final Long getExpirationDate() {
366 return ((long) expirationDate) + MAC_EPOC_FIX;
367 }
368
375 public final Long getCreationDate() {
376 return ((long) creationDate) + MAC_EPOC_FIX;
377 }
378
384 public final String getURL() {
385 return url;
386 }
387
393 public final String getName() {
394 return name;
395 }
396
402 public final String getPath() {
403 return path;
404 }
405
411 public final String getValue() {
412 return value;
413 }
414
424 private String decodeString(byte[] byteArray, int offset) {
425 byte[] stringBytes = new byte[byteArray.length - offset];
426 for (int index = 0; index < stringBytes.length; index++) {
427 byte nibble = byteArray[offset + index];
428 if (nibble != '\0') { //NON-NLS
429 stringBytes[index] = nibble;
430 } else {
431 break;
432 }
433 }
434
435 return new String(stringBytes);
436 }
437 }
438}
synchronized static Logger getLogger(String name)
Definition Logger.java:124

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.