Skip to content
This repository has been archived by the owner on Oct 5, 2019. It is now read-only.

Commit

Permalink
Merge pull request #160 from armtash/remove_cert_chain
Browse files Browse the repository at this point in the history
removed deprecated signature_chain
  • Loading branch information
armtash authored May 10, 2018
2 parents cb9e54a + c40f3bf commit 08a0193
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 324 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ For records representing files there are a bunch of useful keys:
* `md5`: MD5 hash of the file contents.
* `sha1`: SHA1 hash of the file contents.
* `sha2`: SHA2 hash of the file contents.
* `signature_chain`: The common names of the certs in the file's signing chain

For records representing downloaded files:
* `xattr-wherefrom`: A list containing the source and referrer URLs for the downloaded file.
Expand Down
323 changes: 0 additions & 323 deletions osxcollector/osxcollector.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
#
import base64
import calendar
import ctypes
import ctypes.util
import os
import shutil
import struct
Expand All @@ -43,7 +41,6 @@

import Foundation
import macholib.MachO
import objc
from xattr import getxattr

__version__ = '1.9'
Expand Down Expand Up @@ -301,10 +298,6 @@ def _get_file_info(file_path, log_xattr=False):
mtime = _datetime_to_string(datetime.fromtimestamp(os.path.getmtime(file_path)))
ctime = _datetime_to_string(datetime.fromtimestamp(os.path.getctime(file_path)))
md5_hash, sha1_hash, sha2_hash = _hash_file(file_path)
try:
signature_chain = CodeSignChecker.get_signature_chain(file_path)
except CodeSignChecker.CodeSignCheckerError:
signature_chain = []

# check for extradata
try:
Expand All @@ -324,7 +317,6 @@ def _get_file_info(file_path, log_xattr=False):
'file_path': file_path,
'mtime': mtime,
'ctime': ctime,
'signature_chain': signature_chain,
'extra_data_check': extra_data_check,
'extra_data_found': extra_data_found,
}
Expand Down Expand Up @@ -414,314 +406,6 @@ def _decode_error_description(error):
return cfstring.encode('utf-8', 'ignore')


class CodeSignChecker(object):

"""Call `CodeSignChecker.get_signature_chain` to get the signing chain for a binary.
This class is derived from KnockKock
- https://github.com/synack/knockknock
KnockKnock is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
- https://github.com/synack/knockknock/blob/master/LICENSE
"""

# Class level pointers to dynamic link libraries
SEC_DLL = None
OBJC_DLL = None
FOUNDATION_DLL = None

# OS X constants
errSecSuccess = 0x0
kSecCSCheckAllArchitectures = 0x1
kSecCSDefaultFlags = 0x0
kSecCSDoNotValidateResources = 0x4
kSecCSSigningInformation = 0x2
kSecCSStrictValidate = 0x16
# kSecCSStrictValidate | kSecCSCheckAllArchitectures
kSecCSStrictValidate_kSecCSCheckAllArchitectures = 0x17
# kSecCSStrictValidate | kSecCSCheckAllArchitectures | kSecCSCheckNestedCode
kSecCSStrictValidate_kSecCSCheckAllArchitectures_kSecCSCheckNestedCode = 0x1f
kSecCodeInfoCertificates = 'certificates'

class CodeSignCheckerError(Exception):
pass

class MissingDLLError(CodeSignCheckerError):

"""Raised when a DLL can't be loaded."""
pass

class CheckSignatureError(CodeSignCheckerError):

"""Raised when a signature can't be checked."""
pass

class SystemCallError(CodeSignCheckerError):

"""Raised when a system call fails."""

def __init__(self, method, status):
self.status = status
self.method = method

def __str__(self):
return '{0} failed with status[{1}]'.format(self.method, self.status)

class CFTypeWrapper(object):

"""A helper class which ensures CFRelease is called.
Attributes:
val: The actual value stored in this wrapper.
"""

def __init__(self, val):
self.val = val

def __del__(self):
CFRelease = CodeSignChecker.FOUNDATION_DLL.CFRelease
CFRelease.argtypes = [ctypes.c_void_p]
CFRelease(self.val)

@classmethod
def _load_library(cls, dll_path):
"""Load a DLL.
Args:
dll_path: Fully qualified path to the DLL
Returns:
handle to the library
Raises:
MissingDLLError
"""
dll = ctypes.cdll.LoadLibrary(dll_path)
if not dll:
raise cls.MissingDLLError(message='could not load {0}'.format(dll_path))
return dll

@classmethod
def _load_framework(cls):
"""Loads all DLLs required by the CodeSignChecker."""

if not cls.SEC_DLL:
cls.SEC_DLL = cls._load_library('/System/Library/Frameworks/Security.framework/Versions/Current/Security')

if not cls.OBJC_DLL:
cls.OBJC_DLL = cls._load_library(ctypes.util.find_library('objc'))

cls.OBJC_DLL.objc_getClass.restype = ctypes.c_void_p
cls.OBJC_DLL.sel_registerName.restype = ctypes.c_void_p

if not cls.FOUNDATION_DLL:
cls.FOUNDATION_DLL = cls._load_library(ctypes.util.find_library('Foundation'))

@classmethod
def SecStaticCodeCreateWithPath(cls, file_path):
"""Call Security Framework's SecStaticCodeCreateWithPath method.
Args:
file_path: fully qualified file path
Returns:
A SecStaticCodeRef wrapped with a CFTypeWrapper
"""

if isinstance(file_path, unicode):
file_path = file_path.encode(encoding='utf-8', errors='ignore')

# file_path as NSString
file_path = Foundation.NSString.stringWithUTF8String_(file_path)

# file_path with spaces escaped
file_path = file_path.stringByAddingPercentEscapesUsingEncoding_(Foundation.NSUTF8StringEncoding).encode('utf-8')

# init file_path as url
path = Foundation.NSURL.URLWithString_(Foundation.NSString.stringWithUTF8String_(file_path))

# pointer for static code
static_code = ctypes.c_void_p(0)

# create static code from path and check
result = cls.SEC_DLL.SecStaticCodeCreateWithPath(
ctypes.c_void_p(objc.pyobjc_id(path)), cls.kSecCSDefaultFlags,
ctypes.byref(static_code))
if cls.errSecSuccess != result:
raise cls.SystemCallError('SecStaticCodeCreateWithPath', result)

return cls.CFTypeWrapper(static_code)

@classmethod
def SecStaticCodeCheckValidityWithErrors(cls, static_code):
"""Call Security Framework's SecStaticCodeCheckValidityWithErrors method.
Args:
static_code: A SecStaticCodeRef
Raises:
SystemCallError when the code is not secure
"""
if strict is True:
# strict checking
sigCheckFlags = cls.kSecCSStrictValidate_kSecCSCheckAllArchitectures_kSecCSCheckNestedCode
else:
# no-strict checking
sigCheckFlags = cls.kSecCSDoNotValidateResources

result = cls.SEC_DLL.SecStaticCodeCheckValidityWithErrors(static_code, sigCheckFlags, None, None)
if cls.errSecSuccess != result:
raise cls.SystemCallError('SecStaticCodeCheckValidityWithErrors', result)

@classmethod
def SecCodeCopySigningInformation(cls, static_code):
"""Call Security Framework's SecCodeCopySigningInformation method.
Args:
static_code: A SecStaticCodeRef
Returns:
A CFDictionaryRef wrapped with a CFTypeWrapper
Raises:
SystemCallError
"""

signing_information = ctypes.c_void_p(0)

result = cls.SEC_DLL.SecCodeCopySigningInformation(static_code, cls.kSecCSSigningInformation, ctypes.byref(signing_information))
if cls.errSecSuccess != result:
raise cls.SystemCallError('SecCodeCopySigningInformation', result)

return cls.CFTypeWrapper(signing_information)

@classmethod
def SecCertificateCopyCommonName(cls, certificate):
"""Call Security Framework's SecCertificateCopyCommonName method.
Args:
static_code: A SecCertificateRef
Returns:
An NSString
Raises:
SystemCallError
"""

certificate_name = ctypes.c_char_p(0)

result = cls.SEC_DLL.SecCertificateCopyCommonName(ctypes.c_void_p(certificate), ctypes.byref(certificate_name))
if cls.errSecSuccess != result:
raise cls.SystemCallError('SecCertificateCopyCommonName', result)

return certificate_name

@classmethod
def NSString_from_str(cls, str_val):
"""Creates an instance of NSString.
Args:
str_val: A Python string
Returns:
An NSString
"""
NSString = cls.OBJC_DLL.objc_getClass('NSString')
cls.OBJC_DLL.objc_msgSend.restype = ctypes.c_void_p
cls.OBJC_DLL.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]

return cls.OBJC_DLL.objc_msgSend(NSString, cls.OBJC_DLL.sel_registerName('stringWithUTF8String:'), str_val)

@classmethod
def str_from_NSString(cls, nsstring_val):
"""Creates a Python string from an NSString.
Args:
nsstring_val: An NSString
Returns:
A string
"""
cls.OBJC_DLL.objc_msgSend.restype = ctypes.c_char_p
cls.OBJC_DLL.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p]

return cls.OBJC_DLL.objc_msgSend(nsstring_val, cls.OBJC_DLL.sel_registerName('UTF8String'))

@classmethod
def CFDictionary_objectForKey(cls, instance, key):
"""Calls CFDictionary:objectForKey
Args:
instance - A CFDictionaryRef
key - A string
Returns:
value retrieved from the CFDictionary
"""
nsstring_key = cls.NSString_from_str(key)

cls.OBJC_DLL.objc_msgSend.restype = ctypes.c_void_p
cls.OBJC_DLL.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]

return cls.OBJC_DLL.objc_msgSend(instance, cls.OBJC_DLL.sel_registerName('objectForKey:'), nsstring_key)

@classmethod
def CFArray_count(cls, instance):
"""Calls CFArray:count
Args:
instance - A CFArrayRef
Returns:
int
"""
cls.OBJC_DLL.objc_msgSend.restype = ctypes.c_uint
cls.OBJC_DLL.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p]

return cls.OBJC_DLL.objc_msgSend(instance, cls.OBJC_DLL.sel_registerName('count'))

@classmethod
def CFArray_objectAtIndex(cls, instance, index):
"""Calls CFArray:objectAtIndex
Args:
instance - A CFArrayRef
index - int
Returns:
value retrieved from the CFArray
"""
cls.OBJC_DLL.objc_msgSend.restype = ctypes.c_void_p
cls.OBJC_DLL.objc_msgSend.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint]

return cls.OBJC_DLL.objc_msgSend(instance, cls.OBJC_DLL.sel_registerName('objectAtIndex:'), index)

@classmethod
def get_signature_chain(cls, file_path):
"""Retrieves the singing authorities for a binary.
Args:
file_path: A string of the fully qualified file path
Returns:
An array of signing authorities or an empty array
"""
signing_authorities = []

cls._load_framework()

static_code = cls.SecStaticCodeCreateWithPath(file_path)

try:
cls.SecStaticCodeCheckValidityWithErrors(static_code.val)
except cls.SystemCallError:
# The binary is not signed
return signing_authorities

cfdict_information = cls.SecCodeCopySigningInformation(static_code.val)
cfarray_cert_chain = cls.CFDictionary_objectForKey(cfdict_information.val, cls.kSecCodeInfoCertificates)

for index in xrange(cls.CFArray_count(cfarray_cert_chain)):
certificate = cls.CFArray_objectAtIndex(cfarray_cert_chain, index)

try:
nsstring_common_name = cls.SecCertificateCopyCommonName(certificate)
common_name = cls.str_from_NSString(nsstring_common_name)
signing_authorities.append(common_name)
except cls.SystemCallError:
# If this certificate's name can't be retrieved just continue
pass

return signing_authorities


class DictUtils(object):

"""A set of method for manipulating dictionaries."""
Expand Down Expand Up @@ -1843,7 +1527,6 @@ def main():

global DEBUG_MODE
global ROOT_PATH
global strict

global firefox_ignored_sqlite_keys
global safari_ignored_sqlite_keys
Expand Down Expand Up @@ -1872,10 +1555,6 @@ def main():
parser.add_argument(
'-d', '--debug', action='store_true', default=False,
help='[OPTIONAL] Enable verbose output and python breakpoints.')
parser.add_argument(
'-t', '--strict', dest='strict', default=False, action='store_true',
help='[OPTIONAL] Enable strict codesign checking of applications and '
'binaries')
parser.add_argument(
'-c', '--collect-cookies', dest='collect_cookies_value',
default=False, action='store_true',
Expand All @@ -1888,8 +1567,6 @@ def main():
'local storage')
args = parser.parse_args()

strict = args.strict

DEBUG_MODE = args.debug
ROOT_PATH = args.rootpath

Expand Down

0 comments on commit 08a0193

Please sign in to comment.