#!/usr/bin/python # -*- coding: utf-8 -*- # # Copyright 2014 The Plaso Project Authors. # Please see the AUTHORS file for details on individual authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Parser for the Firefox Cookie database.""" from plaso.events import time_events from plaso.lib import errors from plaso.lib import eventdata from plaso.lib import timelib # Register the cookie plugins. from plaso.parsers import cookie_plugins # pylint: disable=unused-import from plaso.parsers import sqlite from plaso.parsers.cookie_plugins import interface as cookie_interface from plaso.parsers.sqlite_plugins import interface class FirefoxCookieEvent(time_events.TimestampEvent): """Convenience class for a Firefox Cookie event.""" DATA_TYPE = 'firefox:cookie:entry' def __init__( self, timestamp, usage, identifier, hostname, cookie_name, value, path, secure, httponly): """Initializes the event. Args: timestamp: The timestamp value in WebKit format.. usage: Timestamp description string. identifier: The row identifier. hostname: The hostname of host that set the cookie value. cookie_name: The name field of the cookie. value: The value of the cookie. path: An URI of the page that set the cookie. secure: Indication if this cookie should only be transmitted over a secure channel. httponly: An indication that the cookie cannot be accessed through client side script. """ super(FirefoxCookieEvent, self).__init__(timestamp, usage) if hostname.startswith('.'): hostname = hostname[1:] self.offset = identifier self.host = hostname self.cookie_name = cookie_name self.data = value self.path = path self.secure = True if secure else False self.httponly = True if httponly else False if self.secure: scheme = u'https' else: scheme = u'http' self.url = u'{0:s}://{1:s}{2:s}'.format(scheme, hostname, path) class FirefoxCookiePlugin(interface.SQLitePlugin): """Parse Firefox Cookies file.""" NAME = 'firefox_cookies' DESCRIPTION = u'Parser for Firefox cookies SQLite database files.' # Define the needed queries. QUERIES = [ (('SELECT id, baseDomain, name, value, host, path, expiry, lastAccessed, ' 'creationTime, isSecure, isHttpOnly FROM moz_cookies'), 'ParseCookieRow')] # The required tables common to Archived History and History. REQUIRED_TABLES = frozenset(['moz_cookies']) # Point to few sources for URL information. URLS = [ (u'https://hg.mozilla.org/mozilla-central/file/349a2f003529/netwerk/' u'cookie/nsCookie.h')] def __init__(self): """Initializes a plugin object.""" super(FirefoxCookiePlugin, self).__init__() self._cookie_plugins = cookie_interface.GetPlugins() def ParseCookieRow( self, parser_context, row, file_entry=None, parser_chain=None, query=None, **unused_kwargs): """Parses a cookie row. Args: parser_context: A parser context object (instance of ParserContext). row: The row resulting from the query. file_entry: Optional file entry object (instance of dfvfs.FileEntry). The default is None. parser_chain: Optional string containing the parsing chain up to this point. The default is None. query: Optional query string. The default is None. """ if row['creationTime']: event_object = FirefoxCookieEvent( row['creationTime'], eventdata.EventTimestamp.CREATION_TIME, row['id'], row['host'], row['name'], row['value'], row['path'], row['isSecure'], row['isHttpOnly']) parser_context.ProduceEvent( event_object, query=query, parser_chain=parser_chain, file_entry=file_entry) if row['lastAccessed']: event_object = FirefoxCookieEvent( row['lastAccessed'], eventdata.EventTimestamp.ACCESS_TIME, row['id'], row['host'], row['name'], row['value'], row['path'], row['isSecure'], row['isHttpOnly']) parser_context.ProduceEvent( event_object, query=query, parser_chain=parser_chain, file_entry=file_entry) if row['expiry']: # Expiry time (nsCookieService::GetExpiry in # netwerk/cookie/nsCookieService.cpp). # It's calculated as the difference between the server time and the time # the server wants the cookie to expire and adding that difference to the # client time. This localizes the client time regardless of whether or not # the TZ environment variable was set on the client. timestamp = timelib.Timestamp.FromPosixTime(row['expiry']) event_object = FirefoxCookieEvent( timestamp, u'Cookie Expires', row['id'], row['host'], row['name'], row['value'], row['path'], row['isSecure'], row['isHttpOnly']) parser_context.ProduceEvent( event_object, query=query, parser_chain=parser_chain, file_entry=file_entry) # Go through all cookie plugins to see if there are is any specific parsing # needed. hostname = row['host'] if hostname.startswith('.'): hostname = hostname[1:] url = u'http{0:s}://{1:s}{2:s}'.format( u's' if row['isSecure'] else u'', hostname, row['path']) for cookie_plugin in self._cookie_plugins: try: cookie_plugin.Process( parser_context, cookie_name=row['name'], cookie_data=row['value'], url=url, file_entry=file_entry, parser_chain=parser_chain) except errors.WrongPlugin: pass sqlite.SQLiteParser.RegisterPlugin(FirefoxCookiePlugin)