OLD | NEW |
| (Empty) |
1 # -*- coding: utf-8 -*- | |
2 """This file contains preprocessors for Mac OS X.""" | |
3 | |
4 import abc | |
5 import plistlib | |
6 | |
7 from plaso.containers import artifacts | |
8 from plaso.lib import errors | |
9 from plaso.lib import plist | |
10 from plaso.parsers.plist_plugins import interface as plist_interface | |
11 from plaso.preprocessors import interface | |
12 from plaso.preprocessors import manager | |
13 | |
14 | |
15 class PlistPreprocessPlugin(interface.FileSystemPreprocessPlugin): | |
16 """The plist preprocess plugin interface.""" | |
17 | |
18 _PLIST_PATH = u'' | |
19 | |
20 def _GetPlistRootKey(self, file_entry): | |
21 """Opens a plist file entry. | |
22 | |
23 Args: | |
24 file_entry (dfvfs.FileEntry): file entry of the plist. | |
25 | |
26 Returns: | |
27 plistlib._InternalDict: plist root key. | |
28 | |
29 Raises: | |
30 errors.PreProcessFail: if the preprocessing fails. | |
31 """ | |
32 file_object = file_entry.GetFileObject() | |
33 | |
34 try: | |
35 plist_file = plist.PlistFile() | |
36 plist_file.Read(file_object) | |
37 | |
38 except IOError as exception: | |
39 location = getattr(file_entry.path_spec, u'location', u'') | |
40 raise errors.PreProcessFail( | |
41 u'Unable to read plist file: {0:s} with error: {1:s}'.format( | |
42 location, exception)) | |
43 | |
44 finally: | |
45 file_object.close() | |
46 | |
47 return plist_file.root_key | |
48 | |
49 @abc.abstractmethod | |
50 def Run(self, searcher, knowledge_base): | |
51 """Determines the value of the preprocessing attributes. | |
52 | |
53 Args: | |
54 searcher (dfvfs.FileSystemSearcher): file system searcher. | |
55 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
56 """ | |
57 | |
58 | |
59 class PlistKeyPreprocessPlugin(PlistPreprocessPlugin): | |
60 """The plist key preprocess plugin interface. | |
61 | |
62 The plist key preprocess plugin retieves values from key names, | |
63 defined in _PLIST_KEYS, from a specific plist file, defined in | |
64 _PLIST_PATH. | |
65 """ | |
66 | |
67 # The key that's value should be returned back. It is an ordered list | |
68 # of preference. If the first value is found it will be returned and no | |
69 # others will be searched. | |
70 _PLIST_KEYS = [u''] | |
71 | |
72 def _FindKeys(self, key, names, matches): | |
73 """Searches the plist key hierarchy for keys with matching names. | |
74 | |
75 If a match is found a tuple of the key name and value is added to | |
76 the matches list. | |
77 | |
78 Args: | |
79 key (plistlib._InternalDict): plist key. | |
80 names (list[str]): names of the keys to match. | |
81 matches (list[str]): keys with matching names. | |
82 """ | |
83 for name, subkey in iter(key.items()): | |
84 if name in names: | |
85 matches.append((name, subkey)) | |
86 | |
87 # pylint: disable=protected-access | |
88 if isinstance(subkey, plistlib._InternalDict): | |
89 self._FindKeys(subkey, names, matches) | |
90 | |
91 @abc.abstractmethod | |
92 def _ParseValue(self, knowledge_base, name, value): | |
93 """Parses a plist key value. | |
94 | |
95 Args: | |
96 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
97 name (str): name of the plist key. | |
98 value (str): value of the plist key. | |
99 """ | |
100 | |
101 def Run(self, searcher, knowledge_base): | |
102 """Determines the value of the preprocessing attributes. | |
103 | |
104 Args: | |
105 searcher (dfvfs.FileSystemSearcher): file system searcher. | |
106 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
107 | |
108 Raises: | |
109 errors.PreProcessFail: if the preprocessing fails. | |
110 """ | |
111 file_entry = self._FindFileEntry(searcher, self._PLIST_PATH) | |
112 if not file_entry: | |
113 return | |
114 | |
115 root_key = self._GetPlistRootKey(file_entry) | |
116 if not root_key: | |
117 location = getattr(file_entry.path_spec, u'location', u'') | |
118 raise errors.PreProcessFail( | |
119 u'Missing root key in plist: {0:s}'.format(location)) | |
120 | |
121 matches = [] | |
122 | |
123 self._FindKeys(root_key, self._PLIST_KEYS, matches) | |
124 if not matches: | |
125 raise errors.PreProcessFail(u'No such keys: {0:s}.'.format( | |
126 u', '.join(self._PLIST_KEYS))) | |
127 | |
128 name = None | |
129 value = None | |
130 for name, value in matches: | |
131 if value: | |
132 break | |
133 | |
134 if value is None: | |
135 raise errors.PreProcessFail(u'No values found for keys: {0:s}.'.format( | |
136 u', '.join(self._PLIST_KEYS))) | |
137 | |
138 self._ParseValue(knowledge_base, name, value) | |
139 | |
140 | |
141 class MacOSXHostnamePreprocessPlugin(PlistKeyPreprocessPlugin): | |
142 """Mac OS X hostname preprocessing plugin.""" | |
143 | |
144 _PLIST_PATH = u'/Library/Preferences/SystemConfiguration/preferences.plist' | |
145 _PLIST_KEYS = [u'ComputerName', u'LocalHostName'] | |
146 | |
147 def _ParseValue(self, knowledge_base, name, value): | |
148 """Parses a plist key value. | |
149 | |
150 Args: | |
151 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
152 name (str): name of the plist key. | |
153 value (str): value of the plist key. | |
154 """ | |
155 if name not in self._PLIST_KEYS: | |
156 return | |
157 | |
158 hostname_artifact = artifacts.HostnameArtifact(name=value) | |
159 knowledge_base.SetHostname(hostname_artifact) | |
160 | |
161 | |
162 class MacOSXKeyboardLayoutPreprocessPlugin(PlistKeyPreprocessPlugin): | |
163 """Mac OS X keyboard layout preprocessing plugin.""" | |
164 | |
165 _PLIST_PATH = u'/Library/Preferences/com.apple.HIToolbox.plist' | |
166 _PLIST_KEYS = [u'AppleCurrentKeyboardLayoutInputSourceID'] | |
167 | |
168 def _ParseValue(self, knowledge_base, name, value): | |
169 """Parses a plist key value. | |
170 | |
171 Args: | |
172 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
173 name (str): name of the plist key. | |
174 value (str): value of the plist key. | |
175 """ | |
176 if name not in self._PLIST_KEYS: | |
177 return | |
178 | |
179 if isinstance(value, (list, tuple)): | |
180 value = value[0] | |
181 | |
182 _, _, keyboard_layout = value.rpartition(u'.') | |
183 | |
184 knowledge_base.SetValue(u'keyboard_layout', keyboard_layout) | |
185 | |
186 | |
187 class MacOSXSystemVersionPreprocessPlugin(PlistKeyPreprocessPlugin): | |
188 """Mac OS X system version information preprocessing plugin.""" | |
189 | |
190 _PLIST_PATH = u'/System/Library/CoreServices/SystemVersion.plist' | |
191 _PLIST_KEYS = [u'ProductUserVisibleVersion'] | |
192 | |
193 def _ParseValue(self, knowledge_base, name, value): | |
194 """Parses a plist key value. | |
195 | |
196 Args: | |
197 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
198 name (str): name of the plist key. | |
199 value (str): value of the plist key. | |
200 """ | |
201 if name not in self._PLIST_KEYS: | |
202 return | |
203 | |
204 knowledge_base.SetValue(u'operating_system_version', value) | |
205 | |
206 | |
207 class MacOSXTimeZonePreprocessPlugin(interface.FileSystemPreprocessPlugin): | |
208 """Mac OS X time zone preprocessing plugin.""" | |
209 | |
210 _PATH = u'/private/etc/localtime' | |
211 | |
212 def Run(self, searcher, knowledge_base): | |
213 """Determines the value of the preprocessing attributes. | |
214 | |
215 Args: | |
216 searcher (dfvfs.FileSystemSearcher): file system searcher. | |
217 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
218 | |
219 Raises: | |
220 errors.PreProcessFail: if the preprocessing fails. | |
221 """ | |
222 file_entry = self._FindFileEntry(searcher, self._PATH) | |
223 if not file_entry: | |
224 return | |
225 | |
226 if not file_entry.link: | |
227 raise errors.PreProcessFail( | |
228 u'Unable to retrieve time zone information from: {0:s}.'.format( | |
229 self._PATH)) | |
230 | |
231 _, _, time_zone = file_entry.link.partition(u'zoneinfo/') | |
232 if not time_zone: | |
233 return | |
234 | |
235 try: | |
236 knowledge_base.SetTimeZone(time_zone) | |
237 except ValueError: | |
238 # TODO: add and store preprocessing errors. | |
239 pass | |
240 | |
241 | |
242 class MacOSXUserAccountsPreprocessPlugin(PlistPreprocessPlugin): | |
243 """Mac OS X user accouns preprocessing plugin.""" | |
244 | |
245 ATTRIBUTE = u'users' | |
246 | |
247 # Define the path to the user account information. | |
248 _PLIST_PATH_REGEX = ( | |
249 u'/private/var/db/dslocal/nodes/Default/users/[^_].+.plist') | |
250 | |
251 _KEYS = frozenset([u'gid', 'home', u'name', u'realname', u'shell', u'uid']) | |
252 | |
253 def _GetKeysDefaultEmpty(self, top_level, keys, depth=1): | |
254 """Return keys nested in a plist dict, defaulting to an empty value. | |
255 | |
256 The method GetKeys fails if the supplied key does not exist within the | |
257 plist object. This alternate method behaves the same way as GetKeys | |
258 except that instead of raising an error if the key doesn't exist it will | |
259 assign an empty string value ('') to the field. | |
260 | |
261 Args: | |
262 top_level (plistlib._InternalDict): top level plist object. | |
263 keys (set[str]): names of keys that should be returned. | |
264 depth (int): depth within the plist, where 1 is top level. | |
265 | |
266 Returns: | |
267 dict[str,str]: values of the requested keys. | |
268 """ | |
269 keys = set(keys) | |
270 match = {} | |
271 | |
272 if depth == 1: | |
273 for key in keys: | |
274 value = top_level.get(key, None) | |
275 if value is not None: | |
276 match[key] = value | |
277 else: | |
278 for _, parsed_key, parsed_value in plist_interface.RecurseKey( | |
279 top_level, depth=depth): | |
280 if parsed_key in keys: | |
281 match[parsed_key] = parsed_value | |
282 if set(match.keys()) == keys: | |
283 return match | |
284 return match | |
285 | |
286 def _ParsePlistFileEntry(self, knowledge_base, file_entry): | |
287 """Parses an user account plist file. | |
288 | |
289 Args: | |
290 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
291 file_entry (dfvfs.FileNetry): file entry of the user account plist file. | |
292 | |
293 Raises: | |
294 errors.PreProcessFail: if the preprocessing fails. | |
295 """ | |
296 root_key = self._GetPlistRootKey(file_entry) | |
297 if not root_key: | |
298 location = getattr(file_entry.path_spec, u'location', u'') | |
299 raise errors.PreProcessFail( | |
300 u'Missing root key in plist: {0:s}'.format(location)) | |
301 | |
302 try: | |
303 match = self._GetKeysDefaultEmpty(root_key, self._KEYS) | |
304 except KeyError as exception: | |
305 location = getattr(file_entry.path_spec, u'location', u'') | |
306 raise errors.PreProcessFail( | |
307 u'Unable to read user plist file: {0:s} with error: {1:s}'.format( | |
308 location, exception)) | |
309 | |
310 name = match.get(u'name', [None])[0] | |
311 uid = match.get(u'uid', [None])[0] | |
312 | |
313 if not name or not uid: | |
314 # TODO: add and store preprocessing errors. | |
315 return | |
316 | |
317 user_account = artifacts.UserAccountArtifact( | |
318 identifier=uid, username=name) | |
319 user_account.group_identifier = match.get(u'gid', [None])[0] | |
320 user_account.full_name = match.get(u'realname', [None])[0] | |
321 user_account.shell = match.get(u'shell', [None])[0] | |
322 user_account.user_directory = match.get(u'home', [None])[0] | |
323 | |
324 try: | |
325 knowledge_base.AddUserAccount(user_account) | |
326 except KeyError: | |
327 # TODO: add and store preprocessing errors. | |
328 pass | |
329 | |
330 def Run(self, searcher, knowledge_base): | |
331 """Determines the value of the preprocessing attributes. | |
332 | |
333 Args: | |
334 searcher (dfvfs.FileSystemSearcher): file system searcher. | |
335 knowledge_base (KnowledgeBase): to fill with preprocessing information. | |
336 """ | |
337 path_specs = self._FindPathSpecs(searcher, self._PLIST_PATH_REGEX) | |
338 if not path_specs: | |
339 return | |
340 | |
341 for path_spec in path_specs: | |
342 file_entry = searcher.GetFileEntryByPathSpec(path_spec) | |
343 self._ParsePlistFileEntry(knowledge_base, file_entry) | |
344 | |
345 | |
346 manager.PreprocessPluginsManager.RegisterPlugins([ | |
347 MacOSXHostnamePreprocessPlugin, MacOSXKeyboardLayoutPreprocessPlugin, | |
348 MacOSXSystemVersionPreprocessPlugin, MacOSXTimeZonePreprocessPlugin, | |
349 MacOSXUserAccountsPreprocessPlugin]) | |
OLD | NEW |