apparmor/parser/parser_common.c
John Johansen f86fda02f5 parser: fix 16 bit state limitation
The hfa stores next/check transitions in 16 bit fields to reduce memory
usage. However this means the state machine can on contain 2^16
states.

Allow the next/check tables to be 32 bit. This theoretically could allow
for 2^32 states however the base table uses the top 8 bits as flags
giving us only 2^24 bits to index into the next/check tables. With
most states having at least 1 transition this effectively caps the
number of states at 2^24.

To obtain 2^32 possible states a flags table needs to be added. Add
a skeleton around supporting a flags table, so we can note the remaining
work that needs to be done. This patch will only allow for 2^24 states.

Bug: https://gitlab.com/apparmor/apparmor/-/issues/419

Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-14 17:01:30 -07:00

236 lines
7.5 KiB
C

/*
* Copyright (c) 2010 - 2012
* Canonical Ltd. (All rights reserved)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc. or Canonical,
* Ltd.
*/
#include <iostream>
#include <stdlib.h>
#include <stdarg.h>
#include "parser.h"
#include "file_cache.h"
/* Policy versioning is determined by a combination of 3 values:
* policy_version: version of txt policy
* parser_abi_version: version of abi revision of policy generated by parser
* kernel_abi_version: version of abi revision for the kernel
*
* The version info is stored in a single 32 bit version field in the
* header portion of each binary policy file.
*
* policy_version:
* a gross revision number indicating what features and semantics are
* expected by the text policy. This does not necessarily map directly
* to a feature set as a kernel may not have all the supported features
* patched/builtin.
*
* policy_version is not supported by kernels that only support v5
* kernel abi, so it will not be written when creating policy for
* those kernels.
*
* kernel_abi_version:
* should be set to the highest version supported by both the parser and
* the kernel.
* This allows new kernels to detect old userspaces, and new parsers
* to support old kernels and policies semantics.
*
* parser_abi_version:
* should be bumped when a compiler error or some other event happens
* and policy cache needs to be forced to be recomputed, when the
* policy_version or kernel version has not changed.
*
* parser_abi_version is not supported by kernels that only support
* v5 kernel abi so it will not be written when creating policy for those
* kernels.
*
* Default values set to v5 kernel abi before the different versioning
* numbers where supported.
*/
uint32_t policy_version = 2;
uint32_t parser_abi_version = 2;
uint32_t kernel_abi_version = 5;
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 features_supports_network = 0; /* kernel supports network rules */
int features_supports_networkv8 = 0; /* kernel supports 4.17 network rules */
int features_supports_inet = 0; /* kernel supports inet network rules */
int features_supports_unix = 0; /* kernel supports unix socket rules */
int kernel_supports_policydb = 0; /* kernel supports new policydb */
int features_supports_mount = 0; /* kernel supports mount rules */
int features_supports_dbus = 0; /* kernel supports dbus rules */
int kernel_supports_diff_encode = 0; /* kernel supports diff_encode */
int features_supports_signal = 0; /* kernel supports signal rules */
int features_supports_ptrace = 0; /* kernel supports ptrace rules */
int features_supports_stacking = 0; /* kernel supports stacking */
int features_supports_domain_xattr = 0; /* x attachment cond */
int features_supports_userns = 0; /* kernel supports user namespace */
int features_supports_posix_mqueue = 0; /* kernel supports mqueue rules */
int features_supports_sysv_mqueue = 0; /* kernel supports mqueue rules */
int features_supports_io_uring = 0; /* kernel supports io_uring rules */
int features_supports_flag_interruptible = 0;
int features_supports_flag_signal = 0;
int features_supports_flag_error = 0;
int kernel_supports_oob = 0; /* out of band transitions */
int kernel_supports_promptdev = 0; /* prompt via audit perms */
int kernel_supports_permstable32 = 0; /* extended permissions */
int kernel_supports_permstable32_v1 = 0; /* extended permissions */
int prompt_compat_mode = PROMPT_COMPAT_UNKNOWN;
int kernel_supports_state32 = 0; /* 32 bit state table entries */
int kernel_supports_flags_table = 0; /* state flags stored in table */
int conf_verbose = 0;
int conf_quiet = 0;
int names_only = 0;
int current_lineno = 1;
int option = OPTION_ADD;
const char *progname = __FILE__;
char *profile_ns = NULL;
char *profilename = NULL;
char *current_filename = NULL;
FILE *ofile = NULL;
IncludeCache_t *g_includecache;
optflags parseopts = {
.control = (optflags_t)(CONTROL_DFA_TREE_NORMAL | CONTROL_DFA_TREE_SIMPLE | CONTROL_DFA_MINIMIZE | CONTROL_DFA_DIFF_ENCODE | CONTROL_RULE_MERGE),
.dump = 0,
.warn = DEFAULT_WARNINGS,
.Werror = 0
};
#ifdef FORCE_READ_IMPLIES_EXEC
int read_implies_exec = 1;
#else
int read_implies_exec = 0;
#endif
void pwarnf(bool werr, const char *fmt, ...)
{
va_list arg;
char *newfmt;
if (conf_quiet || names_only || option == OPTION_REMOVE)
return;
if (asprintf(&newfmt, _("%s from %s (%s%sline %d): %s"),
werr ? _("Warning converted to Error") : _("Warning"),
profilename ? profilename : "stdin",
current_filename ? current_filename : "",
current_filename ? " " : "",
current_lineno,
fmt) == -1)
return;
va_start(arg, fmt);
vfprintf(stderr, newfmt, arg);
va_end(arg);
free(newfmt);
if (werr) {
fflush(stderr);
exit(1);
}
}
/* do we want to warn once/profile or just once per compile?? */
void common_warn_once(const char *name, const char *msg, const char **warned_name)
{
if ((parseopts.warn & WARN_RULE_NOT_ENFORCED) && *warned_name != name) {
if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
cerr << "Warning converted to Error";
else
cerr << "Warning";
cerr << " from profile " << name << " (";
if (current_filename)
cerr << current_filename;
else
cerr << "stdin";
cerr << "): " << msg << "\n";
*warned_name = name;
}
if (parseopts.Werror & WARN_RULE_NOT_ENFORCED)
exit(1);
}
bool prompt_compat_mode_supported(int mode)
{
if (mode == PROMPT_COMPAT_PERMSV2 &&
(kernel_supports_permstable32 && !kernel_supports_permstable32_v1))
return true;
/*
else if (mode == PROMPT_COMPAT_DEV &&
kernel_supports_promptdev)
return true;
*/
else if (mode == PROMPT_COMPAT_FLAG &&
kernel_supports_permstable32)
return true;
/*
else if (mode == PROMPT_COMPAT_PERMSV1 &&
(kernel_supports_permstable32_v1))
return true;
*/
else if (mode == PROMPT_COMPAT_IGNORE)
return true;
return false;
}
int default_prompt_compat_mode()
{
if (prompt_compat_mode_supported(PROMPT_COMPAT_PERMSV2))
return PROMPT_COMPAT_PERMSV2;
if (prompt_compat_mode_supported(PROMPT_COMPAT_DEV))
return PROMPT_COMPAT_DEV;
if (prompt_compat_mode_supported(PROMPT_COMPAT_FLAG))
return PROMPT_COMPAT_FLAG;
if (prompt_compat_mode_supported(PROMPT_COMPAT_PERMSV1))
return PROMPT_COMPAT_PERMSV1;
if (prompt_compat_mode_supported(PROMPT_COMPAT_IGNORE))
return PROMPT_COMPAT_IGNORE;
return PROMPT_COMPAT_IGNORE;
}
void print_prompt_compat_mode(FILE *f)
{
switch (prompt_compat_mode) {
case PROMPT_COMPAT_IGNORE:
fprintf(f, "ignore");
break;
case PROMPT_COMPAT_FLAG:
fprintf(f, "flag");
break;
case PROMPT_COMPAT_PERMSV2:
fprintf(f, "permsv2");
break;
case PROMPT_COMPAT_PERMSV1:
fprintf(f, "permsv1");
break;
case PROMPT_COMPAT_DEV:
fprintf(stderr, "dev");
break;
default:
fprintf(f, "Unknown prompt compat mode '%d'", prompt_compat_mode);
}
}