diff --git a/parser/parser.h b/parser/parser.h index 24e01a472..cff0c248c 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -302,6 +302,7 @@ extern int force_complain; extern int perms_create; extern int net_af_max_override; extern int kernel_load; +extern int kernel_supports_setload; extern int kernel_supports_network; extern int kernel_supports_policydb; extern int kernel_supports_diff_encode; diff --git a/parser/parser_common.c b/parser/parser_common.c index bfd297a79..c192bc913 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -66,6 +66,7 @@ int force_complain = 0; int perms_create = 0; /* perms contain create flag */ int net_af_max_override = -1; /* use kernel to determine af_max */ int kernel_load = 1; +int kernel_supports_setload = 0; /* kernel supports atomic set loads */ int kernel_supports_network = 0; /* kernel supports network rules */ int kernel_supports_policydb = 0; /* kernel supports new policydb */ int kernel_supports_mount = 0; /* kernel supports mount rules */ diff --git a/parser/parser_interface.c b/parser/parser_interface.c index 4da57f6bf..b297085ca 100644 --- a/parser/parser_interface.c +++ b/parser/parser_interface.c @@ -634,52 +634,73 @@ static char *next_profile_buffer(char *buffer, int size) return NULL; } +static int write_buffer(int fd, char *buffer, int size, bool set) +{ + const char *err_str = set ? "profile set" : "profile"; + int wsize = write(fd, buffer, size); + if (wsize < 0) { + PERROR(_("%s: Unable to write %s\n"), progname, err_str); + return -errno; + } else if (wsize < size) { + PERROR(_("%s: Unable to write %s\n"), progname, err_str); + return -EPROTO; + } + return 0; +} + int sd_load_buffer(int option, char *buffer, int size) { int fd = -1; - int error = -ENOMEM, wsize, bsize; + int error, bsize; char *filename = NULL; - char *b; + + /* TODO: push backup into caller */ + if (!kernel_load) + return 0; switch (option) { case OPTION_ADD: if (asprintf(&filename, "%s/.load", subdomainbase) == -1) - goto exit; - if (kernel_load) fd = open(filename, O_WRONLY); + return -ENOMEM; break; case OPTION_REPLACE: if (asprintf(&filename, "%s/.replace", subdomainbase) == -1) - goto exit; - if (kernel_load) fd = open(filename, O_WRONLY); + return -ENOMEM; break; default: - error = -EINVAL; - goto exit; - break; + return -EINVAL; } - if (kernel_load && fd < 0) { + fd = open(filename, O_WRONLY); + if (fd < 0) { PERROR(_("Unable to open %s - %s\n"), filename, strerror(errno)); error = -errno; - goto exit; + goto out; } - error = 0; - for (b = buffer; b ; b = next_profile_buffer(b + sizeof(header_version), bsize)) { - bsize = size - (b - buffer); - if (kernel_load) { - wsize = write(fd, b, bsize); - if (wsize < 0) { - error = -errno; - } else if (wsize < bsize) { - PERROR(_("%s: Unable to write entire profile entry\n"), - progname); - } + if (kernel_supports_setload) { + error = write_buffer(fd, buffer, size, true); + } else { + char *b, *next; + + error = 0; /* in case there are no profiles */ + for (b = buffer; b; b = next, size -= bsize) { + next = next_profile_buffer(b + sizeof(header_version), + size); + if (next) + bsize = next - b; + else + bsize = size; + error = write_buffer(fd, b, bsize, false); + if (error) + break; } } - if (kernel_load) close(fd); -exit: + close(fd); + +out: free(filename); + return error; } diff --git a/parser/parser_main.c b/parser/parser_main.c index 2d318e700..e017bd2c3 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -689,6 +689,8 @@ static void set_supported_features(void) { kernel_supports_policydb = 1; if (strstr(features_string, "v6")) kernel_abi_version = 6; + if (strstr(features_string, "set_load")) + kernel_supports_setload = 1; if (strstr(features_string, "network")) kernel_supports_network = 1; if (strstr(features_string, "mount"))