Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added clean flags for JSON rule import #1300

Merged
merged 2 commits into from
Mar 3, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 80 additions & 53 deletions Source/santactl/Commands/SNTCommandRule.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,58 +45,65 @@ + (NSString *)shortHelpText {
}

+ (NSString *)longHelpText {
return (@"Usage: santactl rule [options]\n"
@" One of:\n"
@" --allow: add to allow\n"
@" --block: add to block\n"
@" --silent-block: add to silent block\n"
@" --compiler: allow and mark as a compiler\n"
@" --remove: remove existing rule\n"
@" --check: check for an existing rule\n"
@" --import {path}: import rules from a JSON file\n"
@" --export {path}: export rules to a JSON file\n"
@"\n"
@" One of:\n"
@" --path {path}: path of binary/bundle to add/remove.\n"
@" Will add the hash of the file currently at that path.\n"
@" Does not work with --check. Use the fileinfo verb to check.\n"
@" the rule state of a file.\n"
@" --identifier {sha256|teamID|signingID}: identifier to add/remove/check\n"
@" --sha256 {sha256}: hash to add/remove/check [deprecated]\n"
@"\n"
@" Optionally:\n"
@" --teamid: add or check a team ID rule instead of binary\n"
@" --signingid: add or check a signing ID rule instead of binary (see notes)\n"
@" --certificate: add or check a certificate sha256 rule instead of binary\n"
return (
@"Usage: santactl rule [options]\n"
@" One of:\n"
@" --allow: add to allow\n"
@" --block: add to block\n"
@" --silent-block: add to silent block\n"
@" --compiler: allow and mark as a compiler\n"
@" --remove: remove existing rule\n"
@" --check: check for an existing rule\n"
@" --import {path}: import rules from a JSON file\n"
@" --export {path}: export rules to a JSON file\n"
@"\n"
@" One of:\n"
@" --path {path}: path of binary/bundle to add/remove.\n"
@" Will add the hash of the file currently at that path.\n"
@" Does not work with --check. Use the fileinfo verb to check.\n"
@" the rule state of a file.\n"
@" --identifier {sha256|teamID|signingID}: identifier to add/remove/check\n"
@" --sha256 {sha256}: hash to add/remove/check [deprecated]\n"
@"\n"
@" Optionally:\n"
@" --teamid: add or check a team ID rule instead of binary\n"
@" --signingid: add or check a signing ID rule instead of binary (see notes)\n"
@" --certificate: add or check a certificate sha256 rule instead of binary\n"
#ifdef DEBUG
@" --force: allow manual changes even when SyncBaseUrl is set\n"
@" --force: allow manual changes even when SyncBaseUrl is set\n"
#endif
@" --message {message}: custom message\n"
@"\n"
@" Notes:\n"
@" The format of `identifier` when adding/checking a `signingid` rule is:\n"
@"\n"
@" `TeamID:SigningID`\n"
@"\n"
@" Because signing IDs are controlled by the binary author, this ensures\n"
@" that the signing ID is properly scoped to a developer. For the special\n"
@" case of platform binaries, `TeamID` should be replaced with the string\n"
@" \"platform\" (e.g. `platform:SigningID`). This allows for rules\n"
@" targeting Apple-signed binaries that do not have a team ID.\n"
@"\n"
@" Importing / Exporting Rules:\n"
@" If santa is not configured to use a sync server one can export\n"
@" & import its non-static rules to and from JSON files using the \n"
@" --export/--import flags. These files have the following form:\n"
@"\n"
@" {\"rules\": [{rule-dictionaries}]}\n"
@" e.g. {\"rules\": [\n"
@" {\"policy\": \"BLOCKLIST\",\n"
@" \"identifier\": "
@"\"84de9c61777ca36b13228e2446d53e966096e78db7a72c632b5c185b2ffe68a6\"\n"
@" \"custom_url\" : \"\",\n"
@" \"custom_msg\": \"/bin/ls block for demo\"}\n"
@" ]}\n");
@" --message {message}: custom message\n"
@" --clean: when importing rules via JSON clear all non-transitive rules before importing\n"
@" --clean-all: when importing rules via JSON clear all rules before importing\n"
@"\n"
@" Notes:\n"
@" The format of `identifier` when adding/checking a `signingid` rule is:\n"
@"\n"
@" `TeamID:SigningID`\n"
@"\n"
@" Because signing IDs are controlled by the binary author, this ensures\n"
@" that the signing ID is properly scoped to a developer. For the special\n"
@" case of platform binaries, `TeamID` should be replaced with the string\n"
@" \"platform\" (e.g. `platform:SigningID`). This allows for rules\n"
@" targeting Apple-signed binaries that do not have a team ID.\n"
@"\n"
@" Importing / Exporting Rules:\n"
@" If santa is not configured to use a sync server one can export\n"
@" & import its non-static rules to and from JSON files using the \n"
@" --export/--import flags. These files have the following form:\n"
@"\n"
@" {\"rules\": [{rule-dictionaries}]}\n"
@" e.g. {\"rules\": [\n"
@" {\"policy\": \"BLOCKLIST\",\n"
@" \"identifier\": "
@"\"84de9c61777ca36b13228e2446d53e966096e78db7a72c632b5c185b2ffe68a6\"\n"
@" \"custom_url\" : \"\",\n"
@" \"custom_msg\": \"/bin/ls block for demo\"}\n"
@" ]}\n"
@"\n"
@" By default rules are not cleared when importing. To clear the\n"
@" database you must use either --clean or --clean-all\n"
@"\n");
}

- (void)runWithArguments:(NSArray *)arguments {
Expand All @@ -120,6 +127,7 @@ - (void)runWithArguments:(NSArray *)arguments {
NSString *path;
NSString *jsonFilePath;
BOOL check = NO;
SNTRuleCleanup cleanupType = SNTRuleCleanupNone;
BOOL importRules = NO;
BOOL exportRules = NO;

Expand Down Expand Up @@ -181,6 +189,10 @@ - (void)runWithArguments:(NSArray *)arguments {
[self printErrorUsageAndExit:@"--import requires an argument"];
}
jsonFilePath = arguments[i];
} else if ([arg caseInsensitiveCompare:@"--clean"] == NSOrderedSame) {
cleanupType = SNTRuleCleanupNonTransitive;
} else if ([arg caseInsensitiveCompare:@"--clean-all"] == NSOrderedSame) {
cleanupType = SNTRuleCleanupAll;
} else if ([arg caseInsensitiveCompare:@"--export"] == NSOrderedSame) {
if (importRules) {
[self printErrorUsageAndExit:@"--import and --export are mutually exclusive"];
Expand All @@ -199,12 +211,27 @@ - (void)runWithArguments:(NSArray *)arguments {
}
}

if (!importRules && cleanupType != SNTRuleCleanupNone) {
switch (cleanupType) {
case SNTRuleCleanupNonTransitive:
[self printErrorUsageAndExit:@"--clean can only be used with --import"];
break;
case SNTRuleCleanupAll:
[self printErrorUsageAndExit:@"--clean-all can only be used with --import"];
break;
default:
// This is a programming error.
LOGE(@"Unexpected SNTRuleCleanupType %ld", cleanupType);
exit(EXIT_FAILURE);
}
}

if (jsonFilePath.length > 0) {
if (importRules) {
if (newRule.identifier != nil || path != nil || check) {
[self printErrorUsageAndExit:@"--import can only be used by itself"];
}
[self importJSONFile:jsonFilePath];
[self importJSONFile:jsonFilePath with:cleanupType];
} else if (exportRules) {
if (newRule.identifier != nil || path != nil || check) {
[self printErrorUsageAndExit:@"--export can only be used by itself"];
Expand Down Expand Up @@ -385,7 +412,7 @@ - (void)printStateOfRule:(SNTRule *)rule daemonConnection:(MOLXPCConnection *)da
exit(0);
}

- (void)importJSONFile:(NSString *)jsonFilePath {
- (void)importJSONFile:(NSString *)jsonFilePath with:(SNTRuleCleanup)cleanupType {
// If the file exists parse it and then add the rules one at a time.
NSError *error;
NSData *data = [NSData dataWithContentsOfFile:jsonFilePath options:0 error:&error];
Expand Down Expand Up @@ -422,7 +449,7 @@ - (void)importJSONFile:(NSString *)jsonFilePath {

[[self.daemonConn remoteObjectProxy]
databaseRuleAddRules:parsedRules
ruleCleanup:SNTRuleCleanupNone
ruleCleanup:cleanupType
reply:^(NSError *error) {
if (error) {
printf("Failed to modify rules: %s",
Expand Down
Loading