├── go.mod ├── sp ├── SPExecutionPolicy.h ├── SPKernelExtensionPolicy.h ├── SPExecutionHistoryItem.h ├── SPKernelExtensionPolicyItem.h ├── SystemPolicyWrapper.h ├── system_policy.go └── SystemPolicyWrapper.m ├── .gitignore ├── main.go ├── LICENSE ├── go.sum ├── osquery └── table │ ├── legacyexec │ └── legacy_exec_history.go │ └── kextpolicy │ └── kext_policy.go └── README.md /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/knightsc/system_policy 2 | 3 | go 1.16 4 | 5 | require github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5 6 | -------------------------------------------------------------------------------- /sp/SPExecutionPolicy.h: -------------------------------------------------------------------------------- 1 | #ifndef SPExecutionPolicy_h 2 | #define SPExecutionPolicy_h 3 | 4 | @class SPExecutionHistoryItem; 5 | 6 | @interface SPExecutionPolicy : NSObject 7 | 8 | - (instancetype)init; 9 | - (NSArray *)legacyExecutionHistory; 10 | 11 | @end 12 | 13 | #endif /* SPExecutionPolicy_h */ 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # dep vendor/ 8 | vendor/ 9 | 10 | # build artifacts 11 | build/ 12 | 13 | # Test binary, build with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | .DS_Store 20 | .vscode 21 | 22 | -------------------------------------------------------------------------------- /sp/SPKernelExtensionPolicy.h: -------------------------------------------------------------------------------- 1 | #ifndef SPKernelExtensionPolicy_h 2 | #define SPKernelExtensionPolicy_h 3 | 4 | @class SPKernelExtensionPolicyItem; 5 | 6 | @interface SPKernelExtensionPolicy : NSObject 7 | 8 | - (instancetype)init; 9 | - (NSArray *)currentPolicy; 10 | 11 | @end 12 | 13 | #endif /* SPKernelExtensionPolicy_h */ 14 | -------------------------------------------------------------------------------- /sp/SPExecutionHistoryItem.h: -------------------------------------------------------------------------------- 1 | #ifndef SPExecutionHistoryItem_h 2 | #define SPExecutionHistoryItem_h 3 | 4 | @interface SPExecutionHistoryItem : NSObject 5 | 6 | @property (readonly, nonatomic) NSString *execPath; 7 | @property (readonly, nonatomic) NSString *mmapPath; 8 | @property (readonly, nonatomic) NSString *signingID; 9 | @property (readonly, nonatomic) NSString *teamID; 10 | @property (readonly, nonatomic) NSString *cdHash; 11 | @property (readonly, nonatomic) NSString *responsiblePath; 12 | @property (readonly, nonatomic) NSString *developerName; 13 | @property (readonly, nonatomic) NSDate *lastSeen; 14 | 15 | @end 16 | 17 | #endif /* SPExecutionHistoryItem_h */ 18 | -------------------------------------------------------------------------------- /sp/SPKernelExtensionPolicyItem.h: -------------------------------------------------------------------------------- 1 | #ifndef SPKernelExtensionPolicyItem_h 2 | #define SPKernelExtensionPolicyItem_h 3 | 4 | @interface SPKernelExtensionPolicyItem : NSObject 5 | 6 | @property (readonly, nonatomic) NSString *developerName; 7 | @property (readonly, nonatomic) NSString *applicationName; 8 | @property (readonly, nonatomic) NSString *applicationPath; 9 | @property (readonly, nonatomic) NSString *teamID; 10 | @property (readonly, nonatomic) NSArray *bundleIDs; 11 | @property (nonatomic, getter=isAllowed) char allowed; 12 | @property (readonly, nonatomic, getter=isRebootRequired) char rebootRequired; 13 | @property (readonly, nonatomic, getter=isModified) char modified; 14 | 15 | @end 16 | 17 | #endif /* SPKernelExtensionPolicyItem_h */ 18 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | 7 | osquery "github.com/osquery/osquery-go" 8 | 9 | "github.com/knightsc/system_policy/osquery/table/kextpolicy" 10 | "github.com/knightsc/system_policy/osquery/table/legacyexec" 11 | ) 12 | 13 | func main() { 14 | flSocket := flag.String("socket", "", "") 15 | flag.Int("timeout", 0, "") 16 | flag.Int("interval", 0, "") 17 | flag.Bool("verbose", false, "") 18 | flag.Parse() 19 | 20 | if *flSocket == "" { 21 | log.Fatalln("--socket flag cannot be empty") 22 | } 23 | 24 | server, err := osquery.NewExtensionManagerServer("system_policy", *flSocket) 25 | if err != nil { 26 | log.Fatalf("Error creating osquery extension server: %s\n", err) 27 | } 28 | 29 | server.RegisterPlugin(legacyexec.TablePlugin(), kextpolicy.TablePlugin()) 30 | if err := server.Run(); err != nil { 31 | log.Fatal(err) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Scott Knight 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Microsoft/go-winio v0.4.9 h1:3RbgqgGVqmcpbOiwrjbVtDHLlJBGF6aE+yHmNtBNsFQ= 2 | github.com/Microsoft/go-winio v0.4.9/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= 3 | github.com/apache/thrift v0.13.1-0.20200603211036-eac4d0c79a5f h1:33BV5v3u8I6dA2dEoPuXWCsAaHHOJfPtdxZhAMQV4uo= 4 | github.com/apache/thrift v0.13.1-0.20200603211036-eac4d0c79a5f/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5 h1:E275nJIUAvIK/RSN8cq9MAcRLk23jaZq+s24B0I8bEw= 8 | github.com/osquery/osquery-go v0.0.0-20210622151333-99b4efa62ec5/go.mod h1:JKR5QhjsYdnIPY7hakgas5sxf8qlA/9wQnLqaMfWdcg= 9 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 10 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 14 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 15 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644 h1:CA1DEQ4NdKphKeL70tvsWNdT5oFh1lOjihRcEDROi0I= 16 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 17 | -------------------------------------------------------------------------------- /sp/SystemPolicyWrapper.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class SPExecutionHistoryItem; 4 | @class SPKernelExtensionPolicyItem; 5 | 6 | typedef struct { 7 | void *framework_handle; 8 | NSAutoreleasePool *pool; 9 | NSArray *historyItems; 10 | } history_items; 11 | 12 | typedef struct { 13 | const char *exec_path; 14 | const char *mmap_path; 15 | const char *signing_id; 16 | const char *team_id; 17 | const char *cd_hash; 18 | const char *responsible_path; 19 | const char *developer_name; 20 | double last_seen; 21 | } history_item; 22 | 23 | history_items init_history_items(void); 24 | void release_history_items(history_items self); 25 | unsigned long history_items_length(history_items self); 26 | history_item get_history_item(history_items self, unsigned long index); 27 | 28 | typedef struct { 29 | void *framework_handle; 30 | NSAutoreleasePool *pool; 31 | NSArray *kextItems; 32 | } kext_items; 33 | 34 | typedef struct { 35 | const char *developer_name; 36 | const char *application_name; 37 | const char *application_path; 38 | const char *team_id; 39 | unsigned long bundle_id_count; 40 | char allowed; 41 | char reboot_required; 42 | char modified; 43 | } kext_item; 44 | 45 | kext_items init_kext_items(void); 46 | void release_kext_items(kext_items self); 47 | unsigned long kext_items_length(kext_items self); 48 | kext_item get_kext_item(kext_items self, unsigned long index); 49 | const char *get_kext_bundle_id(kext_items self, unsigned long kextIndex, unsigned long bundleIDIndex); 50 | -------------------------------------------------------------------------------- /osquery/table/legacyexec/legacy_exec_history.go: -------------------------------------------------------------------------------- 1 | package legacyexec 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/knightsc/system_policy/sp" 8 | "github.com/osquery/osquery-go/plugin/table" 9 | ) 10 | 11 | func TablePlugin() *table.Plugin { 12 | columns := []table.ColumnDefinition{ 13 | table.TextColumn("exec_path"), 14 | table.TextColumn("mmap_path"), 15 | table.TextColumn("signing_id"), 16 | table.TextColumn("team_id"), 17 | table.TextColumn("cd_hash"), 18 | table.TextColumn("responsible_path"), 19 | table.TextColumn("developer_name"), 20 | table.TextColumn("last_seen"), 21 | } 22 | 23 | return table.NewPlugin("legacy_exec_history", columns, generate) 24 | } 25 | 26 | func generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) { 27 | results := make([]map[string]string, 0) 28 | 29 | items := sp.LegacyExecutionHistory() 30 | for _, item := range items { 31 | row := map[string]string{} 32 | row["exec_path"] = item.ExecPath 33 | row["last_seen"] = item.LastSeen.Format(time.RFC3339) 34 | if item.MmapPath != "" { 35 | row["mmap_path"] = item.MmapPath 36 | } 37 | if item.SigningID != "" { 38 | row["signing_id"] = item.SigningID 39 | } 40 | if item.TeamID != "" { 41 | row["team_id"] = item.TeamID 42 | } 43 | if item.CDHash != "" { 44 | row["cd_hash"] = item.CDHash 45 | } 46 | if item.ResponsiblePath != "" { 47 | row["responsible_path"] = item.ResponsiblePath 48 | } 49 | if item.DeveloperName != "" { 50 | row["developer_name"] = item.DeveloperName 51 | } 52 | 53 | results = append(results, row) 54 | } 55 | 56 | return results, nil 57 | } 58 | -------------------------------------------------------------------------------- /osquery/table/kextpolicy/kext_policy.go: -------------------------------------------------------------------------------- 1 | package kextpolicy 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/knightsc/system_policy/sp" 7 | "github.com/osquery/osquery-go/plugin/table" 8 | ) 9 | 10 | func TablePlugin() *table.Plugin { 11 | columns := []table.ColumnDefinition{ 12 | table.TextColumn("developer_name"), 13 | table.TextColumn("application_name"), 14 | table.TextColumn("application_path"), 15 | table.TextColumn("team_id"), 16 | table.TextColumn("bundle_id"), 17 | table.IntegerColumn("allowed"), 18 | table.IntegerColumn("reboot_required"), 19 | table.IntegerColumn("modified"), 20 | } 21 | return table.NewPlugin("kext_policy", columns, generate) 22 | } 23 | 24 | func generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) { 25 | results := make([]map[string]string, 0) 26 | 27 | items := sp.CurrentKernelExtensionPolicy() 28 | for _, item := range items { 29 | row := map[string]string{} 30 | row["developer_name"] = item.DeveloperName 31 | if item.ApplicationName != "" { 32 | row["application_name"] = item.ApplicationName 33 | } 34 | if item.ApplicationPath != "" { 35 | row["application_path"] = item.ApplicationPath 36 | } 37 | row["team_id"] = item.TeamID 38 | row["bundle_id"] = item.BundleID 39 | if item.Allowed { 40 | row["allowed"] = "1" 41 | } else { 42 | row["allowed"] = "0" 43 | } 44 | if item.RebootRequired { 45 | row["reboot_required"] = "1" 46 | } else { 47 | row["reboot_required"] = "0" 48 | } 49 | if item.Modified { 50 | row["modified"] = "1" 51 | } else { 52 | row["modified"] = "0" 53 | } 54 | 55 | results = append(results, row) 56 | } 57 | 58 | return results, nil 59 | } 60 | -------------------------------------------------------------------------------- /sp/system_policy.go: -------------------------------------------------------------------------------- 1 | // Package sp provides access to the SystemPolicy.framework on macOS. 2 | package sp 3 | 4 | /* 5 | #cgo CFLAGS: -x objective-c 6 | #cgo LDFLAGS: -framework Foundation 7 | #import "SystemPolicyWrapper.h" 8 | */ 9 | import "C" 10 | import ( 11 | "math" 12 | "time" 13 | ) 14 | 15 | // An ExecutionHistoryItem represents a 32-bit application that has been run. 16 | type ExecutionHistoryItem struct { 17 | ExecPath string 18 | MmapPath string 19 | SigningID string 20 | TeamID string 21 | CDHash string 22 | ResponsiblePath string 23 | DeveloperName string 24 | LastSeen time.Time 25 | } 26 | 27 | // A KernelExtensionPolicyItem represents a KEXT that is either waiting for 28 | // approval to load or is was approved and loaded. 29 | type KernelExtensionPolicyItem struct { 30 | DeveloperName string 31 | ApplicationName string 32 | ApplicationPath string 33 | TeamID string 34 | BundleID string 35 | Allowed bool 36 | RebootRequired bool 37 | Modified bool 38 | } 39 | 40 | // LegacyExecutionHistory returns a list of 32-bit applications that have 41 | // been executed on a macOS machine. 42 | func LegacyExecutionHistory() []ExecutionHistoryItem { 43 | self := C.init_history_items() 44 | defer C.release_history_items(self) 45 | 46 | history := make([]ExecutionHistoryItem, 0) 47 | 48 | length := uint64(C.history_items_length(self)) 49 | for i := uint64(0); i < length; i++ { 50 | item := C.get_history_item(self, C.ulong(i)) 51 | 52 | ehi := ExecutionHistoryItem{} 53 | ehi.ExecPath = C.GoString(item.exec_path) 54 | ehi.MmapPath = C.GoString(item.mmap_path) 55 | ehi.SigningID = C.GoString(item.signing_id) 56 | ehi.TeamID = C.GoString(item.team_id) 57 | ehi.CDHash = C.GoString(item.cd_hash) 58 | ehi.ResponsiblePath = C.GoString(item.responsible_path) 59 | ehi.DeveloperName = C.GoString(item.developer_name) 60 | sec, dec := math.Modf(float64(item.last_seen)) 61 | ehi.LastSeen = time.Unix(int64(sec), int64(dec*(1e9))) 62 | 63 | history = append(history, ehi) 64 | } 65 | 66 | return history 67 | } 68 | 69 | // CurrentKernelExtensionPolicy returns a list of items that represent whether 70 | // a specific KEXT was approved and loaded or not. 71 | func CurrentKernelExtensionPolicy() []KernelExtensionPolicyItem { 72 | self := C.init_kext_items() 73 | defer C.release_kext_items(self) 74 | 75 | policy := make([]KernelExtensionPolicyItem, 0) 76 | 77 | length := uint64(C.kext_items_length(self)) 78 | for i := uint64(0); i < length; i++ { 79 | item := C.get_kext_item(self, C.ulong(i)) 80 | 81 | for j := uint64(0); j < uint64(item.bundle_id_count); j++ { 82 | ki := KernelExtensionPolicyItem{} 83 | ki.DeveloperName = C.GoString(item.developer_name) 84 | ki.ApplicationName = C.GoString(item.application_name) 85 | ki.ApplicationPath = C.GoString(item.application_path) 86 | ki.TeamID = C.GoString(item.team_id) 87 | ki.BundleID = C.GoString(C.get_kext_bundle_id(self, C.ulong(i), C.ulong(j))) 88 | ki.Allowed = item.allowed == '\x01' 89 | ki.RebootRequired = item.reboot_required == '\x01' 90 | ki.Modified = item.modified == '\x01' 91 | 92 | policy = append(policy, ki) 93 | } 94 | } 95 | 96 | return policy 97 | } 98 | -------------------------------------------------------------------------------- /sp/SystemPolicyWrapper.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "SystemPolicyWrapper.h" 3 | #import "SPExecutionPolicy.h" 4 | #import "SPExecutionHistoryItem.h" 5 | #import "SPKernelExtensionPolicy.h" 6 | #import "SPKernelExtensionPolicyItem.h" 7 | 8 | history_items 9 | init_history_items(void) { 10 | history_items self; 11 | self.framework_handle = dlopen("/System/Library/PrivateFrameworks/SystemPolicy.framework/SystemPolicy", RTLD_LAZY); 12 | 13 | if (self.framework_handle != NULL) { 14 | self.pool = [[NSAutoreleasePool alloc] init]; 15 | Class SPExecutionPolicyClass = NSClassFromString(@"SPExecutionPolicy"); 16 | SPExecutionPolicy *execPolicy = [[[SPExecutionPolicyClass alloc] init] autorelease]; 17 | self.historyItems = [execPolicy legacyExecutionHistory]; 18 | } 19 | 20 | return self; 21 | } 22 | 23 | void 24 | release_history_items(history_items self) { 25 | self.historyItems = nil; 26 | 27 | [self.pool release]; 28 | self.pool = nil; 29 | 30 | dlclose(self.framework_handle); 31 | self.framework_handle = NULL; 32 | } 33 | 34 | unsigned long 35 | history_items_length(history_items self) { 36 | return self.historyItems.count; 37 | } 38 | 39 | history_item 40 | get_history_item(history_items self, unsigned long index) { 41 | history_item item; 42 | 43 | if (index < self.historyItems.count) { 44 | item.exec_path = [self.historyItems[index].execPath UTF8String]; 45 | item.mmap_path = [self.historyItems[index].mmapPath UTF8String]; 46 | item.signing_id = [self.historyItems[index].signingID UTF8String]; 47 | item.team_id = [self.historyItems[index].teamID UTF8String]; 48 | item.cd_hash = [self.historyItems[index].cdHash UTF8String]; 49 | item.responsible_path = [self.historyItems[index].responsiblePath UTF8String]; 50 | item.developer_name = [self.historyItems[index].developerName UTF8String]; 51 | item.last_seen = [self.historyItems[index].lastSeen timeIntervalSince1970]; 52 | } 53 | 54 | return item; 55 | } 56 | 57 | kext_items 58 | init_kext_items(void) { 59 | kext_items self; 60 | self.framework_handle = dlopen("/System/Library/PrivateFrameworks/SystemPolicy.framework/SystemPolicy", RTLD_LAZY); 61 | 62 | if (self.framework_handle != NULL) { 63 | self.pool = [[NSAutoreleasePool alloc] init]; 64 | Class SPKernelExtensionPolicyClass = NSClassFromString(@"SPKernelExtensionPolicy"); 65 | SPKernelExtensionPolicy *kextPolicy = [[[SPKernelExtensionPolicyClass alloc] init] autorelease]; 66 | self.kextItems = [kextPolicy currentPolicy]; 67 | } 68 | 69 | return self; 70 | } 71 | 72 | void 73 | release_kext_items(kext_items self) { 74 | self.kextItems = nil; 75 | 76 | [self.pool release]; 77 | self.pool = nil; 78 | 79 | dlclose(self.framework_handle); 80 | self.framework_handle = NULL; 81 | } 82 | 83 | unsigned long 84 | kext_items_length(kext_items self) { 85 | return self.kextItems.count; 86 | } 87 | 88 | kext_item 89 | get_kext_item(kext_items self, unsigned long index) { 90 | kext_item item; 91 | 92 | if (index < self.kextItems.count) { 93 | item.developer_name = [self.kextItems[index].developerName UTF8String]; 94 | item.application_name = [self.kextItems[index].applicationName UTF8String]; 95 | item.application_path = [self.kextItems[index].applicationPath UTF8String]; 96 | item.team_id = [self.kextItems[index].teamID UTF8String]; 97 | item.bundle_id_count = self.kextItems[index].bundleIDs.count; 98 | item.allowed = [self.kextItems[index] isAllowed]; 99 | item.reboot_required = [self.kextItems[index] isRebootRequired]; 100 | item.modified = [self.kextItems[index] isModified]; 101 | } 102 | 103 | return item; 104 | } 105 | 106 | const char * 107 | get_kext_bundle_id(kext_items self, unsigned long kextIndex, unsigned long bundleIDIndex) { 108 | const char *bundle_id = NULL; 109 | 110 | if (kextIndex < self.kextItems.count) { 111 | SPKernelExtensionPolicyItem *item = self.kextItems[kextIndex]; 112 | if (bundleIDIndex < item.bundleIDs.count) { 113 | bundle_id = [item.bundleIDs[bundleIDIndex] UTF8String]; 114 | } 115 | } 116 | return bundle_id; 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # system_policy 2 | 3 | osquery table extension that allows querying of information from the macOS private SystemPolicy.framework. 4 | 5 | ## Introduction 6 | 7 | Beginning with macOS 10.13 Apple started enforcing certain system policies. One of those policies is the requirement that loaded kernel extensions need to be approved by the user. These system policies are also reponsible for keeping track of what 32-bit applications have been run. As announced at the 2018 WWDC, macOS 10.14 will be the last version of macOS to support 32-bit applications. This information can be viewed in the System Information application. 8 | 9 | This information is just stored in sqlite databases located in `/var/db/SystemPolicyConfiguration/`. You can actually query this information yourself from the command line but it does require root access. 10 | 11 | ``` 12 | sudo sqlite3 /var/db/SystemPolicyConfiguration/ExecPolicy 'select * from legacy_exec_history_v4' 13 | ``` 14 | 15 | In order to provide a more convienant method to get this information this osquery table plugin was created. It does not require any special privileges because just like the System Information application it uses the `SystemPolicy.framework` to talk directly to `syspolicyd` to get this information. By making this an osquery extension it allows us to query this information uniformly across an entire fleet of macOS machines. 16 | 17 | ## Extension Overview 18 | 19 | This table extension provides two new read only tables with the following layout: 20 | 21 | ### kext\_policy 22 | Data only returned on macOS 10.13 and higher 23 | 24 | | Column | Type | Description | 25 | |------------------|---------|-----------------------------------------------------| 26 | | developer_name | TEXT | Name of the developer who signed the kext | 27 | | application_name | TEXT | Name of the application that tried to load the kext | 28 | | application_path | TEXT | Path to the application that tried to load the kext | 29 | | team_id | TEXT | The Team ID from the code signing blob | 30 | | bundle_id | TEXT | The Bundle ID of the kext | 31 | | allowed | INTEGER | Whether the kext has been user/mdm approved or not | 32 | | reboot_required | INTEGER | If the kext requires a reboot | 33 | | modified | INTEGER | Unknown | 34 | 35 | ### legacy\_exec\_history 36 | Data only returned on macOS 10.14 and higher 37 | 38 | | Column | Type | Description | 39 | |------------------|------|------------------------------------------------------------------| 40 | | exec_path | TEXT | Path of the application if it was executed directly | 41 | | mmap_path | TEXT | Path of the application if it was loaded into memory | 42 | | signing_id | TEXT | The Signing ID from the code signing blob | 43 | | team_id | TEXT | The Team ID from the code signing blob | 44 | | cd_hash | TEXT | The primary CD Hash from the code signing blob | 45 | | responsible_path | TEXT | Path of the application that launched or mmaped this application | 46 | | developer_name | TEXT | Name from the signing certificate | 47 | | last_seen | TEXT | The timestamp of the last time the applications was used | 48 | 49 | ## Building 50 | 51 | Since this project makes use of the new [modules](https://github.com/golang/go/wiki/Modules) functionality Go 1.11 or above will be required to compile. It should compile on all versions of macOS. Building is as simple as the following: 52 | 53 | ``` 54 | git clone https://github.com/knightsc/system_policy.git 55 | cd system_policy 56 | go build 57 | ``` 58 | 59 | ## Testing 60 | 61 | To test this code, start an osquery shell and find the path of the osquery extension socket: 62 | 63 | ``` 64 | osqueryi --nodisable_extensions 65 | osquery> select value from osquery_flags where name = 'extensions_socket'; 66 | +-----------------------------------+ 67 | | value | 68 | +-----------------------------------+ 69 | | /Users/USERNAME/.osquery/shell.em | 70 | +-----------------------------------+ 71 | ``` 72 | 73 | Then start the Go extension and have it communicate with osqueryi via the extension socket that you retrieved above: 74 | 75 | ``` 76 | ./system_policy -socket /Users/USERNAME/.osquery/shell.em 77 | ``` 78 | 79 | Alternatively, you can also autoload your extension when starting an osquery shell: 80 | 81 | ``` 82 | osqueryi --extension ./system_policy 83 | ``` 84 | 85 | ## Installing 86 | 87 | Installation of this extension should be similar to any other osquery extension. Start by reviewing the [Using Extensions](https://osquery.readthedocs.io/en/stable/deployment/extensions/) guide from the osquery project. 88 | 89 | Essentially you will want to rename the binary to system_policy.ext (or use the pre-built release version) and put it somewhere on your machine with the correct permissions. You can then add an entry for this extension in your autoload file. 90 | --------------------------------------------------------------------------------