2024-08-02 23:08:16 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/parser.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/ctype.h>
|
|
|
|
|
|
|
|
#include "policy.h"
|
|
|
|
#include "policy_parser.h"
|
2024-08-02 23:08:27 -07:00
|
|
|
#include "digest.h"
|
2024-08-02 23:08:16 -07:00
|
|
|
|
|
|
|
#define START_COMMENT '#'
|
|
|
|
#define IPE_POLICY_DELIM " \t"
|
|
|
|
#define IPE_LINE_DELIM "\n\r"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* new_parsed_policy() - Allocate and initialize a parsed policy.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * a pointer to the ipe_parsed_policy structure - Success
|
|
|
|
* * %-ENOMEM - Out of memory (OOM)
|
|
|
|
*/
|
|
|
|
static struct ipe_parsed_policy *new_parsed_policy(void)
|
|
|
|
{
|
|
|
|
struct ipe_parsed_policy *p = NULL;
|
|
|
|
struct ipe_op_table *t = NULL;
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
|
|
if (!p)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
p->global_default_action = IPE_ACTION_INVALID;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
|
|
|
|
t = &p->rules[i];
|
|
|
|
|
|
|
|
t->default_action = IPE_ACTION_INVALID;
|
|
|
|
INIT_LIST_HEAD(&t->rules);
|
|
|
|
}
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* remove_comment() - Truncate all chars following START_COMMENT in a string.
|
|
|
|
*
|
|
|
|
* @line: Supplies a policy line string for preprocessing.
|
|
|
|
*/
|
|
|
|
static void remove_comment(char *line)
|
|
|
|
{
|
|
|
|
line = strchr(line, START_COMMENT);
|
|
|
|
|
|
|
|
if (line)
|
|
|
|
*line = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* remove_trailing_spaces() - Truncate all trailing spaces in a string.
|
|
|
|
*
|
|
|
|
* @line: Supplies a policy line string for preprocessing.
|
|
|
|
*
|
|
|
|
* Return: The length of truncated string.
|
|
|
|
*/
|
|
|
|
static size_t remove_trailing_spaces(char *line)
|
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
i = strlen(line);
|
|
|
|
while (i > 0 && isspace(line[i - 1]))
|
|
|
|
i--;
|
|
|
|
|
|
|
|
line[i] = '\0';
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse_version() - Parse policy version.
|
|
|
|
* @ver: Supplies a version string to be parsed.
|
|
|
|
* @p: Supplies the partial parsed policy.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * %0 - Success
|
|
|
|
* * %-EBADMSG - Version string is invalid
|
|
|
|
* * %-ERANGE - Version number overflow
|
|
|
|
* * %-EINVAL - Parsing error
|
|
|
|
*/
|
|
|
|
static int parse_version(char *ver, struct ipe_parsed_policy *p)
|
|
|
|
{
|
|
|
|
u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };
|
|
|
|
size_t sep_count = 0;
|
|
|
|
char *token;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
while ((token = strsep(&ver, ".")) != NULL) {
|
|
|
|
/* prevent overflow */
|
|
|
|
if (sep_count >= ARRAY_SIZE(cv))
|
|
|
|
return -EBADMSG;
|
|
|
|
|
|
|
|
rc = kstrtou16(token, 10, cv[sep_count]);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
++sep_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* prevent underflow */
|
|
|
|
if (sep_count != ARRAY_SIZE(cv))
|
|
|
|
return -EBADMSG;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum header_opt {
|
|
|
|
IPE_HEADER_POLICY_NAME = 0,
|
|
|
|
IPE_HEADER_POLICY_VERSION,
|
|
|
|
__IPE_HEADER_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
static const match_table_t header_tokens = {
|
|
|
|
{IPE_HEADER_POLICY_NAME, "policy_name=%s"},
|
|
|
|
{IPE_HEADER_POLICY_VERSION, "policy_version=%s"},
|
|
|
|
{__IPE_HEADER_MAX, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse_header() - Parse policy header information.
|
|
|
|
* @line: Supplies header line to be parsed.
|
|
|
|
* @p: Supplies the partial parsed policy.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * %0 - Success
|
|
|
|
* * %-EBADMSG - Header string is invalid
|
|
|
|
* * %-ENOMEM - Out of memory (OOM)
|
|
|
|
* * %-ERANGE - Version number overflow
|
|
|
|
* * %-EINVAL - Version parsing error
|
|
|
|
*/
|
|
|
|
static int parse_header(char *line, struct ipe_parsed_policy *p)
|
|
|
|
{
|
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
char *t, *ver = NULL;
|
|
|
|
size_t idx = 0;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
while ((t = strsep(&line, IPE_POLICY_DELIM)) != NULL) {
|
|
|
|
int token;
|
|
|
|
|
|
|
|
if (*t == '\0')
|
|
|
|
continue;
|
|
|
|
if (idx >= __IPE_HEADER_MAX) {
|
|
|
|
rc = -EBADMSG;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
token = match_token(t, header_tokens, args);
|
|
|
|
if (token != idx) {
|
|
|
|
rc = -EBADMSG;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (token) {
|
|
|
|
case IPE_HEADER_POLICY_NAME:
|
|
|
|
p->name = match_strdup(&args[0]);
|
|
|
|
if (!p->name)
|
|
|
|
rc = -ENOMEM;
|
|
|
|
break;
|
|
|
|
case IPE_HEADER_POLICY_VERSION:
|
|
|
|
ver = match_strdup(&args[0]);
|
|
|
|
if (!ver) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = parse_version(ver, p);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = -EBADMSG;
|
|
|
|
}
|
|
|
|
if (rc)
|
|
|
|
goto out;
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx != __IPE_HEADER_MAX)
|
|
|
|
rc = -EBADMSG;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(ver);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* token_default() - Determine if the given token is "DEFAULT".
|
|
|
|
* @token: Supplies the token string to be compared.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * %false - The token is not "DEFAULT"
|
|
|
|
* * %true - The token is "DEFAULT"
|
|
|
|
*/
|
|
|
|
static bool token_default(char *token)
|
|
|
|
{
|
|
|
|
return !strcmp(token, "DEFAULT");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* free_rule() - Free the supplied ipe_rule struct.
|
|
|
|
* @r: Supplies the ipe_rule struct to be freed.
|
|
|
|
*
|
|
|
|
* Free a ipe_rule struct @r. Note @r must be removed from any lists before
|
|
|
|
* calling this function.
|
|
|
|
*/
|
|
|
|
static void free_rule(struct ipe_rule *r)
|
|
|
|
{
|
|
|
|
struct ipe_prop *p, *t;
|
|
|
|
|
|
|
|
if (IS_ERR_OR_NULL(r))
|
|
|
|
return;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(p, t, &r->props, next) {
|
|
|
|
list_del(&p->next);
|
2024-08-02 23:08:27 -07:00
|
|
|
ipe_digest_free(p->value);
|
2024-08-02 23:08:16 -07:00
|
|
|
kfree(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const match_table_t operation_tokens = {
|
|
|
|
{IPE_OP_EXEC, "op=EXECUTE"},
|
|
|
|
{IPE_OP_FIRMWARE, "op=FIRMWARE"},
|
|
|
|
{IPE_OP_KERNEL_MODULE, "op=KMODULE"},
|
|
|
|
{IPE_OP_KEXEC_IMAGE, "op=KEXEC_IMAGE"},
|
|
|
|
{IPE_OP_KEXEC_INITRAMFS, "op=KEXEC_INITRAMFS"},
|
|
|
|
{IPE_OP_POLICY, "op=POLICY"},
|
|
|
|
{IPE_OP_X509, "op=X509_CERT"},
|
|
|
|
{IPE_OP_INVALID, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse_operation() - Parse the operation type given a token string.
|
|
|
|
* @t: Supplies the token string to be parsed.
|
|
|
|
*
|
|
|
|
* Return: The parsed operation type.
|
|
|
|
*/
|
|
|
|
static enum ipe_op_type parse_operation(char *t)
|
|
|
|
{
|
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
|
|
|
|
return match_token(t, operation_tokens, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const match_table_t action_tokens = {
|
|
|
|
{IPE_ACTION_ALLOW, "action=ALLOW"},
|
|
|
|
{IPE_ACTION_DENY, "action=DENY"},
|
|
|
|
{IPE_ACTION_INVALID, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse_action() - Parse the action type given a token string.
|
|
|
|
* @t: Supplies the token string to be parsed.
|
|
|
|
*
|
|
|
|
* Return: The parsed action type.
|
|
|
|
*/
|
|
|
|
static enum ipe_action_type parse_action(char *t)
|
|
|
|
{
|
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
|
|
|
|
return match_token(t, action_tokens, args);
|
|
|
|
}
|
|
|
|
|
2024-08-02 23:08:20 -07:00
|
|
|
static const match_table_t property_tokens = {
|
|
|
|
{IPE_PROP_BOOT_VERIFIED_FALSE, "boot_verified=FALSE"},
|
|
|
|
{IPE_PROP_BOOT_VERIFIED_TRUE, "boot_verified=TRUE"},
|
2024-08-02 23:08:27 -07:00
|
|
|
{IPE_PROP_DMV_ROOTHASH, "dmverity_roothash=%s"},
|
|
|
|
{IPE_PROP_DMV_SIG_FALSE, "dmverity_signature=FALSE"},
|
|
|
|
{IPE_PROP_DMV_SIG_TRUE, "dmverity_signature=TRUE"},
|
2024-08-02 23:08:30 -07:00
|
|
|
{IPE_PROP_FSV_DIGEST, "fsverity_digest=%s"},
|
|
|
|
{IPE_PROP_FSV_SIG_FALSE, "fsverity_signature=FALSE"},
|
|
|
|
{IPE_PROP_FSV_SIG_TRUE, "fsverity_signature=TRUE"},
|
2024-08-02 23:08:20 -07:00
|
|
|
{IPE_PROP_INVALID, NULL}
|
|
|
|
};
|
|
|
|
|
2024-08-02 23:08:16 -07:00
|
|
|
/**
|
|
|
|
* parse_property() - Parse a rule property given a token string.
|
|
|
|
* @t: Supplies the token string to be parsed.
|
|
|
|
* @r: Supplies the ipe_rule the parsed property will be associated with.
|
|
|
|
*
|
2024-08-02 23:08:20 -07:00
|
|
|
* This function parses and associates a property with an IPE rule based
|
|
|
|
* on a token string.
|
2024-08-02 23:08:16 -07:00
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * %0 - Success
|
|
|
|
* * %-ENOMEM - Out of memory (OOM)
|
|
|
|
* * %-EBADMSG - The supplied token cannot be parsed
|
|
|
|
*/
|
|
|
|
static int parse_property(char *t, struct ipe_rule *r)
|
|
|
|
{
|
2024-08-02 23:08:20 -07:00
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
struct ipe_prop *p = NULL;
|
|
|
|
int rc = 0;
|
|
|
|
int token;
|
2024-08-02 23:08:27 -07:00
|
|
|
char *dup = NULL;
|
2024-08-02 23:08:20 -07:00
|
|
|
|
|
|
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
token = match_token(t, property_tokens, args);
|
|
|
|
|
|
|
|
switch (token) {
|
2024-08-02 23:08:27 -07:00
|
|
|
case IPE_PROP_DMV_ROOTHASH:
|
2024-08-02 23:08:30 -07:00
|
|
|
case IPE_PROP_FSV_DIGEST:
|
2024-08-02 23:08:27 -07:00
|
|
|
dup = match_strdup(&args[0]);
|
|
|
|
if (!dup) {
|
|
|
|
rc = -ENOMEM;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
p->value = ipe_digest_parse(dup);
|
|
|
|
if (IS_ERR(p->value)) {
|
|
|
|
rc = PTR_ERR(p->value);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
fallthrough;
|
2024-08-02 23:08:20 -07:00
|
|
|
case IPE_PROP_BOOT_VERIFIED_FALSE:
|
|
|
|
case IPE_PROP_BOOT_VERIFIED_TRUE:
|
2024-08-02 23:08:27 -07:00
|
|
|
case IPE_PROP_DMV_SIG_FALSE:
|
|
|
|
case IPE_PROP_DMV_SIG_TRUE:
|
2024-08-02 23:08:30 -07:00
|
|
|
case IPE_PROP_FSV_SIG_FALSE:
|
|
|
|
case IPE_PROP_FSV_SIG_TRUE:
|
2024-08-02 23:08:20 -07:00
|
|
|
p->type = token;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = -EBADMSG;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
list_add_tail(&p->next, &r->props);
|
|
|
|
|
2024-08-02 23:08:27 -07:00
|
|
|
out:
|
|
|
|
kfree(dup);
|
2024-08-02 23:08:20 -07:00
|
|
|
return rc;
|
|
|
|
err:
|
|
|
|
kfree(p);
|
2024-08-02 23:08:27 -07:00
|
|
|
goto out;
|
2024-08-02 23:08:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* parse_rule() - parse a policy rule line.
|
|
|
|
* @line: Supplies rule line to be parsed.
|
|
|
|
* @p: Supplies the partial parsed policy.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * 0 - Success
|
|
|
|
* * %-ENOMEM - Out of memory (OOM)
|
|
|
|
* * %-EBADMSG - Policy syntax error
|
|
|
|
*/
|
|
|
|
static int parse_rule(char *line, struct ipe_parsed_policy *p)
|
|
|
|
{
|
|
|
|
enum ipe_action_type action = IPE_ACTION_INVALID;
|
|
|
|
enum ipe_op_type op = IPE_OP_INVALID;
|
|
|
|
bool is_default_rule = false;
|
|
|
|
struct ipe_rule *r = NULL;
|
|
|
|
bool first_token = true;
|
|
|
|
bool op_parsed = false;
|
|
|
|
int rc = 0;
|
|
|
|
char *t;
|
|
|
|
|
|
|
|
if (IS_ERR_OR_NULL(line))
|
|
|
|
return -EBADMSG;
|
|
|
|
|
|
|
|
r = kzalloc(sizeof(*r), GFP_KERNEL);
|
|
|
|
if (!r)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&r->next);
|
|
|
|
INIT_LIST_HEAD(&r->props);
|
|
|
|
|
|
|
|
while (t = strsep(&line, IPE_POLICY_DELIM), line) {
|
|
|
|
if (*t == '\0')
|
|
|
|
continue;
|
|
|
|
if (first_token && token_default(t)) {
|
|
|
|
is_default_rule = true;
|
|
|
|
} else {
|
|
|
|
if (!op_parsed) {
|
|
|
|
op = parse_operation(t);
|
|
|
|
if (op == IPE_OP_INVALID)
|
|
|
|
rc = -EBADMSG;
|
|
|
|
else
|
|
|
|
op_parsed = true;
|
|
|
|
} else {
|
|
|
|
rc = parse_property(t, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
first_token = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
action = parse_action(t);
|
|
|
|
if (action == IPE_ACTION_INVALID) {
|
|
|
|
rc = -EBADMSG;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_default_rule) {
|
|
|
|
if (!list_empty(&r->props)) {
|
|
|
|
rc = -EBADMSG;
|
|
|
|
} else if (op == IPE_OP_INVALID) {
|
|
|
|
if (p->global_default_action != IPE_ACTION_INVALID)
|
|
|
|
rc = -EBADMSG;
|
|
|
|
else
|
|
|
|
p->global_default_action = action;
|
|
|
|
} else {
|
|
|
|
if (p->rules[op].default_action != IPE_ACTION_INVALID)
|
|
|
|
rc = -EBADMSG;
|
|
|
|
else
|
|
|
|
p->rules[op].default_action = action;
|
|
|
|
}
|
|
|
|
} else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) {
|
|
|
|
r->op = op;
|
|
|
|
r->action = action;
|
|
|
|
} else {
|
|
|
|
rc = -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
if (!is_default_rule)
|
|
|
|
list_add_tail(&r->next, &p->rules[op].rules);
|
|
|
|
else
|
|
|
|
free_rule(r);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
err:
|
|
|
|
free_rule(r);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ipe_free_parsed_policy() - free a parsed policy structure.
|
|
|
|
* @p: Supplies the parsed policy.
|
|
|
|
*/
|
|
|
|
void ipe_free_parsed_policy(struct ipe_parsed_policy *p)
|
|
|
|
{
|
|
|
|
struct ipe_rule *pp, *t;
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
if (IS_ERR_OR_NULL(p))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(p->rules); ++i)
|
|
|
|
list_for_each_entry_safe(pp, t, &p->rules[i].rules, next) {
|
|
|
|
list_del(&pp->next);
|
|
|
|
free_rule(pp);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(p->name);
|
|
|
|
kfree(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* validate_policy() - validate a parsed policy.
|
|
|
|
* @p: Supplies the fully parsed policy.
|
|
|
|
*
|
|
|
|
* Given a policy structure that was just parsed, validate that all
|
|
|
|
* operations have their default rules or a global default rule is set.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * %0 - Success
|
|
|
|
* * %-EBADMSG - Policy is invalid
|
|
|
|
*/
|
|
|
|
static int validate_policy(const struct ipe_parsed_policy *p)
|
|
|
|
{
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
if (p->global_default_action != IPE_ACTION_INVALID)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
|
|
|
|
if (p->rules[i].default_action == IPE_ACTION_INVALID)
|
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ipe_parse_policy() - Given a string, parse the string into an IPE policy.
|
|
|
|
* @p: partially filled ipe_policy structure to populate with the result.
|
|
|
|
* it must have text and textlen set.
|
|
|
|
*
|
|
|
|
* Return:
|
|
|
|
* * %0 - Success
|
|
|
|
* * %-EBADMSG - Policy is invalid
|
|
|
|
* * %-ENOMEM - Out of Memory
|
|
|
|
* * %-ERANGE - Policy version number overflow
|
|
|
|
* * %-EINVAL - Policy version parsing error
|
|
|
|
*/
|
|
|
|
int ipe_parse_policy(struct ipe_policy *p)
|
|
|
|
{
|
|
|
|
struct ipe_parsed_policy *pp = NULL;
|
|
|
|
char *policy = NULL, *dup = NULL;
|
|
|
|
bool header_parsed = false;
|
|
|
|
char *line = NULL;
|
|
|
|
size_t len;
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (!p->textlen)
|
|
|
|
return -EBADMSG;
|
|
|
|
|
|
|
|
policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
|
|
|
|
if (!policy)
|
|
|
|
return -ENOMEM;
|
|
|
|
dup = policy;
|
|
|
|
|
|
|
|
pp = new_parsed_policy();
|
|
|
|
if (IS_ERR(pp)) {
|
|
|
|
rc = PTR_ERR(pp);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) {
|
|
|
|
remove_comment(line);
|
|
|
|
len = remove_trailing_spaces(line);
|
|
|
|
if (!len)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!header_parsed) {
|
|
|
|
rc = parse_header(line, pp);
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
header_parsed = true;
|
|
|
|
} else {
|
|
|
|
rc = parse_rule(line, pp);
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!header_parsed || validate_policy(pp)) {
|
|
|
|
rc = -EBADMSG;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->parsed = pp;
|
|
|
|
|
|
|
|
out:
|
|
|
|
kfree(dup);
|
|
|
|
return rc;
|
|
|
|
err:
|
|
|
|
ipe_free_parsed_policy(pp);
|
|
|
|
goto out;
|
|
|
|
}
|