2014-08-23 23:55:12 -07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014
|
|
|
|
* 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 <iomanip>
|
|
|
|
#include <string>
|
|
|
|
#include <sstream>
|
|
|
|
#include <map>
|
2023-08-22 18:12:29 -03:00
|
|
|
#include <arpa/inet.h>
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2015-03-25 17:09:26 -05:00
|
|
|
#include "lib.h"
|
2014-08-23 23:55:12 -07:00
|
|
|
#include "parser.h"
|
|
|
|
#include "profile.h"
|
|
|
|
#include "network.h"
|
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
#define ALL_TYPES 0x43e
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
int parse_net_perms(const char *str_mode, perm32_t *mode, int fail)
|
parser: first step implementing fine grained mediation for unix domain sockets
This patch implements parsing of fine grained mediation for unix domain
sockets, that have abstract and anonymous paths. Sockets with file
system paths are handled by regular file access rules.
The unix network rules follow the general fine grained network
rule pattern of
[<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
specifically for af_unix this is
[<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
<qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ]
<access expr> = ( <access> | <access list> )
<access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' |
'connect' | 'shutdown' | 'getattr' | 'setattr' |
'getopt' | 'setopt' |
'send' | 'receive' | 'r' | 'w' | 'rw' )
(some access modes are incompatible with some rules or require additional
parameters)
<access list> = '(' <access> ( [','] <WS> <access> )* ')'
<WS> = white space
<rule conds> = ( <type cond> | <protocol cond> )*
each cond can appear at most once
<type cond> = 'type' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<protocol cond> = 'protocol' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<local expr> = ( <path cond> | <attr cond> | <opt cond> )*
each cond can appear at most once
<peer expr> = 'peer' '=' ( <path cond> | <label cond> )+
each cond can appear at most once
<path cond> = 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<label cond> = 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')')
<attr cond> = 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<opt cond> = 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<AARE> = ?*[]{}^ ( see man page )
unix domain socket rules are accumulated so that the granted unix
socket permissions are the union of all the listed unix rule permissions.
unix domain socket rules are broad and general and become more restrictive
as further information is specified. Policy may be specified down to
the path and label level. The content of the communication is not
examined.
Some permissions are not compatible with all unix rules.
unix socket rule permissions are implied when a rule does not explicitly
state an access list. By default if a rule does not have an access list
all permissions that are compatible with the specified set of local
and peer conditionals are implied.
The 'server', 'r', 'w' and 'rw' permissions are aliases for other permissions.
server = (create, bind, listen, accept)
r = (receive, getattr, getopt)
w = (create, connect, send, setattr, setopt)
In addition it supports the v7 kernel abi semantics around generic
network rules. The v7 abi removes the masking unix and netlink
address families from the generic masking and uses fine grained
mediation for an address type if supplied.
This means that the rules
network unix,
network netlink,
are now enforced instead of ignored. The parser previously could accept
these but the kernel would ignore anything written to them. If a network
rule is supplied it takes precedence over the finer grained mediation
rule. If permission is not granted via a broad network access rule
fine grained mediation is applied.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 13:22:26 -07:00
|
|
|
{
|
2021-06-09 00:56:59 -07:00
|
|
|
return parse_X_perms("net", AA_VALID_NET_PERMS, str_mode, mode, fail);
|
parser: first step implementing fine grained mediation for unix domain sockets
This patch implements parsing of fine grained mediation for unix domain
sockets, that have abstract and anonymous paths. Sockets with file
system paths are handled by regular file access rules.
The unix network rules follow the general fine grained network
rule pattern of
[<qualifiers>] af_name [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
specifically for af_unix this is
[<qualifiers>] 'unix' [<access expr>] [<rule conds>] [<local expr>] [<peer expr>]
<qualifiers> = [ 'audit' ] [ 'allow' | 'deny' ]
<access expr> = ( <access> | <access list> )
<access> = ( 'server' | 'create' | 'bind' | 'listen' | 'accept' |
'connect' | 'shutdown' | 'getattr' | 'setattr' |
'getopt' | 'setopt' |
'send' | 'receive' | 'r' | 'w' | 'rw' )
(some access modes are incompatible with some rules or require additional
parameters)
<access list> = '(' <access> ( [','] <WS> <access> )* ')'
<WS> = white space
<rule conds> = ( <type cond> | <protocol cond> )*
each cond can appear at most once
<type cond> = 'type' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<protocol cond> = 'protocol' '=' ( <AARE> | '(' ( '"' <AARE> '"' | <AARE> )+ ')' )
<local expr> = ( <path cond> | <attr cond> | <opt cond> )*
each cond can appear at most once
<peer expr> = 'peer' '=' ( <path cond> | <label cond> )+
each cond can appear at most once
<path cond> = 'path' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<label cond> = 'label' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')')
<attr cond> = 'attr' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<opt cond> = 'opt' '=' ( <AARE> | '(' '"' <AARE> '"' | <AARE> ')' )
<AARE> = ?*[]{}^ ( see man page )
unix domain socket rules are accumulated so that the granted unix
socket permissions are the union of all the listed unix rule permissions.
unix domain socket rules are broad and general and become more restrictive
as further information is specified. Policy may be specified down to
the path and label level. The content of the communication is not
examined.
Some permissions are not compatible with all unix rules.
unix socket rule permissions are implied when a rule does not explicitly
state an access list. By default if a rule does not have an access list
all permissions that are compatible with the specified set of local
and peer conditionals are implied.
The 'server', 'r', 'w' and 'rw' permissions are aliases for other permissions.
server = (create, bind, listen, accept)
r = (receive, getattr, getopt)
w = (create, connect, send, setattr, setopt)
In addition it supports the v7 kernel abi semantics around generic
network rules. The v7 abi removes the masking unix and netlink
address families from the generic masking and uses fine grained
mediation for an address type if supplied.
This means that the rules
network unix,
network netlink,
are now enforced instead of ignored. The parser previously could accept
these but the kernel would ignore anything written to them. If a network
rule is supplied it takes precedence over the finer grained mediation
rule. If permission is not granted via a broad network access rule
fine grained mediation is applied.
Signed-off-by: John Johansen <john.johansen@canonical.com>
Acked-by: Seth Arnold <seth.arnold@canonical.com>
2014-09-03 13:22:26 -07:00
|
|
|
}
|
|
|
|
|
2014-08-23 23:55:12 -07:00
|
|
|
/* Bleah C++ doesn't have non-trivial designated initializers so we just
|
|
|
|
* have to make sure these are in order. This means we are more brittle
|
|
|
|
* but there isn't much we can do.
|
|
|
|
*/
|
2014-08-23 23:55:33 -07:00
|
|
|
struct sock_type_map {
|
|
|
|
const char *name;
|
|
|
|
int value;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sock_type_map sock_types[] = {
|
|
|
|
{ "none", 0 },
|
|
|
|
{ "stream", SOCK_STREAM },
|
|
|
|
{ "dgram", SOCK_DGRAM },
|
|
|
|
{ "raw", SOCK_RAW },
|
|
|
|
{ "rdm", SOCK_RDM },
|
|
|
|
{ "seqpacket", SOCK_SEQPACKET },
|
|
|
|
{ "dccp", SOCK_DCCP },
|
|
|
|
{ "invalid", -1 },
|
|
|
|
{ "invalid", -1 },
|
|
|
|
{ "invalid", -1 },
|
|
|
|
{ "packet", SOCK_PACKET },
|
|
|
|
{ NULL, -1 },
|
2014-08-23 23:55:12 -07:00
|
|
|
/*
|
|
|
|
* See comment above
|
|
|
|
*/
|
|
|
|
};
|
|
|
|
|
2014-08-23 23:55:33 -07:00
|
|
|
int net_find_type_val(const char *type)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; sock_types[i].name; i++) {
|
|
|
|
if (strcmp(sock_types[i].name, type) == 0)
|
|
|
|
return sock_types[i].value;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *net_find_type_name(int type)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; sock_types[i].name; i++) {
|
|
|
|
if (sock_types[i].value == type)
|
|
|
|
return sock_types[i].name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-23 23:55:12 -07:00
|
|
|
|
|
|
|
/* FIXME: currently just treating as a bit mask this will have to change
|
|
|
|
* set up a table of mappings, there can be several mappings for a
|
|
|
|
* given match.
|
|
|
|
* currently the mapping does not set the protocol for stream/dgram to
|
|
|
|
* anything other than 0.
|
|
|
|
* network inet tcp -> network inet stream 0 instead of
|
|
|
|
* network inet raw tcp.
|
|
|
|
* some entries are just provided for completeness at this time
|
|
|
|
*/
|
|
|
|
/* values stolen from /etc/protocols - needs to change */
|
|
|
|
#define RAW_TCP 6
|
|
|
|
#define RAW_UDP 17
|
|
|
|
#define RAW_ICMP 1
|
|
|
|
#define RAW_ICMPv6 58
|
|
|
|
|
|
|
|
/* used by af_name.h to auto generate table entries for "name", AF_NAME
|
|
|
|
* pair */
|
|
|
|
#define AA_GEN_NET_ENT(name, AF) \
|
|
|
|
{name, AF, "stream", SOCK_STREAM, "", 0xffffff}, \
|
|
|
|
{name, AF, "dgram", SOCK_DGRAM, "", 0xffffff}, \
|
|
|
|
{name, AF, "seqpacket", SOCK_SEQPACKET, "", 0xffffff}, \
|
|
|
|
{name, AF, "rdm", SOCK_RDM, "", 0xffffff}, \
|
|
|
|
{name, AF, "raw", SOCK_RAW, "", 0xffffff}, \
|
|
|
|
{name, AF, "packet", SOCK_PACKET, "", 0xffffff},
|
|
|
|
/*FIXME: missing {name, AF, "dccp", SOCK_DCCP, "", 0xfffffff}, */
|
|
|
|
|
|
|
|
static struct network_tuple network_mappings[] = {
|
|
|
|
/* basic types */
|
|
|
|
#include "af_names.h"
|
|
|
|
/* FIXME: af_names.h is missing AF_LLC, AF_TIPC */
|
|
|
|
/* mapped types */
|
|
|
|
{"inet", AF_INET, "raw", SOCK_RAW,
|
2023-06-26 14:39:39 -03:00
|
|
|
"tcp", 1 << RAW_TCP},
|
2014-08-23 23:55:12 -07:00
|
|
|
{"inet", AF_INET, "raw", SOCK_RAW,
|
|
|
|
"udp", 1 << RAW_UDP},
|
|
|
|
{"inet", AF_INET, "raw", SOCK_RAW,
|
|
|
|
"icmp", 1 << RAW_ICMP},
|
|
|
|
{"inet", AF_INET, "tcp", SOCK_STREAM,
|
|
|
|
"", 0xffffffff}, /* should we give raw tcp too? */
|
|
|
|
{"inet", AF_INET, "udp", SOCK_DGRAM,
|
|
|
|
"", 0xffffffff}, /* should these be open masks? */
|
|
|
|
{"inet", AF_INET, "icmp", SOCK_RAW,
|
|
|
|
"", 1 << RAW_ICMP},
|
|
|
|
{"inet6", AF_INET6, "tcp", SOCK_STREAM,
|
|
|
|
"", 0xffffffff},
|
|
|
|
{"inet6", AF_INET6, "udp", SOCK_DGRAM,
|
|
|
|
"", 0xffffffff},
|
|
|
|
/* what do we do with icmp on inet6?
|
|
|
|
{"inet6", AF_INET, "icmp", SOCK_RAW, 0},
|
|
|
|
{"inet6", AF_INET, "icmpv6", SOCK_RAW, 0},
|
|
|
|
*/
|
|
|
|
/* terminate */
|
|
|
|
{NULL, 0, NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* The apparmor kernel patches up until 2.6.38 didn't handle networking
|
|
|
|
* tables with sizes > AF_MAX correctly. This could happen when the
|
|
|
|
* parser was built against newer kernel headers and then used to load
|
|
|
|
* policy on an older kernel. This could happen during upgrades or
|
|
|
|
* in multi-kernel boot systems.
|
|
|
|
*
|
|
|
|
* Try to detect the running kernel version and use that to determine
|
|
|
|
* AF_MAX
|
|
|
|
*/
|
|
|
|
#define PROC_VERSION "/proc/sys/kernel/osrelease"
|
|
|
|
static size_t kernel_af_max(void) {
|
|
|
|
char buffer[32];
|
|
|
|
int major;
|
2015-03-25 17:09:26 -05:00
|
|
|
autoclose int fd = -1;
|
|
|
|
int res;
|
2014-08-23 23:55:12 -07:00
|
|
|
|
|
|
|
if (!net_af_max_override) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* the override parameter is specifying the max value */
|
|
|
|
if (net_af_max_override > 0)
|
|
|
|
return net_af_max_override;
|
|
|
|
|
|
|
|
fd = open(PROC_VERSION, O_RDONLY);
|
2014-10-25 16:26:07 -04:00
|
|
|
if (fd == -1)
|
2014-08-23 23:55:12 -07:00
|
|
|
/* fall back to default provided during build */
|
|
|
|
return 0;
|
|
|
|
res = read(fd, &buffer, sizeof(buffer) - 1);
|
|
|
|
if (res <= 0)
|
|
|
|
return 0;
|
|
|
|
buffer[res] = '\0';
|
|
|
|
res = sscanf(buffer, "2.6.%d", &major);
|
|
|
|
if (res != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch(major) {
|
|
|
|
case 24:
|
|
|
|
case 25:
|
|
|
|
case 26:
|
|
|
|
return 34;
|
|
|
|
case 27:
|
|
|
|
return 35;
|
|
|
|
case 28:
|
|
|
|
case 29:
|
|
|
|
case 30:
|
|
|
|
return 36;
|
|
|
|
case 31:
|
|
|
|
case 32:
|
|
|
|
case 33:
|
|
|
|
case 34:
|
|
|
|
case 35:
|
|
|
|
return 37;
|
|
|
|
case 36:
|
|
|
|
case 37:
|
|
|
|
return 38;
|
|
|
|
/* kernels .38 and later should handle this correctly so no
|
|
|
|
* static mapping needed
|
|
|
|
*/
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Yuck. We grab AF_* values to define above from linux/socket.h because
|
|
|
|
* they are more accurate than sys/socket.h for what the kernel actually
|
|
|
|
* supports. However, we can't just include linux/socket.h directly,
|
|
|
|
* because the AF_* definitions are protected with an ifdef KERNEL
|
|
|
|
* wrapper, but we don't want to define that because that can cause
|
|
|
|
* other redefinitions from glibc. However, because the kernel may have
|
|
|
|
* more definitions than glibc, we need make sure AF_MAX reflects this,
|
|
|
|
* hence the wrapping function.
|
|
|
|
*/
|
|
|
|
size_t get_af_max() {
|
|
|
|
size_t af_max;
|
|
|
|
/* HACK: declare that version without "create" had a static AF_MAX */
|
|
|
|
if (!perms_create && !net_af_max_override)
|
|
|
|
net_af_max_override = -1;
|
|
|
|
|
|
|
|
#if AA_AF_MAX > AF_MAX
|
|
|
|
af_max = AA_AF_MAX;
|
|
|
|
#else
|
|
|
|
af_max = AF_MAX;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* HACK: some kernels didn't handle network tables from parsers
|
|
|
|
* compiled against newer kernel headers as they are larger than
|
|
|
|
* the running kernel expected. If net_override is defined check
|
|
|
|
* to see if there is a static max specified for that kernel
|
|
|
|
*/
|
|
|
|
if (net_af_max_override) {
|
|
|
|
size_t max = kernel_af_max();
|
|
|
|
if (max && max < af_max)
|
|
|
|
return max;
|
|
|
|
}
|
|
|
|
|
|
|
|
return af_max;
|
|
|
|
}
|
2023-06-26 14:39:39 -03:00
|
|
|
|
|
|
|
const char *net_find_af_name(unsigned int af)
|
2014-08-23 23:55:12 -07:00
|
|
|
{
|
2023-06-26 14:39:39 -03:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (af < 0 || af > get_af_max())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {
|
|
|
|
if (network_mappings[i].family == af)
|
|
|
|
return network_mappings[i].family_name;
|
2014-08-23 23:55:12 -07:00
|
|
|
}
|
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-08-24 00:00:28 -07:00
|
|
|
|
2024-03-14 17:06:12 -03:00
|
|
|
const char *net_find_protocol_name(unsigned int protocol)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(network_mappings) / sizeof(*network_mappings); i++) {
|
|
|
|
if (network_mappings[i].protocol == protocol) {
|
|
|
|
return network_mappings[i].protocol_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-25 15:29:20 -07:00
|
|
|
const struct network_tuple *net_find_mapping(const struct network_tuple *map,
|
|
|
|
const char *family,
|
2014-08-24 00:00:28 -07:00
|
|
|
const char *type,
|
|
|
|
const char *protocol)
|
2014-08-23 23:55:12 -07:00
|
|
|
{
|
2014-08-25 15:29:20 -07:00
|
|
|
if (!map)
|
|
|
|
map = network_mappings;
|
|
|
|
else
|
|
|
|
/* assumes it points to last entry returned */
|
|
|
|
map++;
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2014-08-25 15:29:20 -07:00
|
|
|
for (; map->family_name; map++) {
|
2014-08-23 23:55:12 -07:00
|
|
|
if (family) {
|
2014-08-25 15:29:20 -07:00
|
|
|
PDEBUG("Checking family %s\n", map->family_name);
|
|
|
|
if (strcmp(family, map->family_name) != 0)
|
2014-08-23 23:55:12 -07:00
|
|
|
continue;
|
|
|
|
PDEBUG("Found family %s\n", family);
|
|
|
|
}
|
|
|
|
if (type) {
|
2014-08-25 15:29:20 -07:00
|
|
|
PDEBUG("Checking type %s\n", map->type_name);
|
|
|
|
if (strcmp(type, map->type_name) != 0)
|
2014-08-23 23:55:12 -07:00
|
|
|
continue;
|
|
|
|
PDEBUG("Found type %s\n", type);
|
|
|
|
}
|
|
|
|
if (protocol) {
|
2014-08-24 00:00:28 -07:00
|
|
|
/* allows the proto to be the "type", ie. tcp implies
|
|
|
|
* stream */
|
|
|
|
if (!type) {
|
2014-08-25 15:29:20 -07:00
|
|
|
PDEBUG("Checking protocol type %s\n", map->type_name);
|
|
|
|
if (strcmp(protocol, map->type_name) == 0)
|
2014-08-24 00:00:28 -07:00
|
|
|
goto match;
|
|
|
|
}
|
2014-08-25 15:29:20 -07:00
|
|
|
PDEBUG("Checking type %s protocol %s\n", map->type_name, map->protocol_name);
|
|
|
|
if (strcmp(protocol, map->protocol_name) != 0)
|
2014-08-23 23:55:12 -07:00
|
|
|
continue;
|
|
|
|
/* fixme should we allow specifying protocol by #
|
|
|
|
* without needing the protocol mapping? */
|
|
|
|
}
|
2014-08-24 00:00:28 -07:00
|
|
|
|
|
|
|
/* if we get this far we have a match */
|
|
|
|
match:
|
2014-08-25 15:29:20 -07:00
|
|
|
return map;
|
2014-08-24 00:00:28 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-08-22 18:12:29 -03:00
|
|
|
bool parse_ipv4_address(const char *input, struct ip_address *result)
|
|
|
|
{
|
|
|
|
struct in_addr addr;
|
|
|
|
if (inet_pton(AF_INET, input, &addr) == 1) {
|
|
|
|
result->family = AF_INET;
|
|
|
|
result->address.address_v4 = addr.s_addr;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parse_ipv6_address(const char *input, struct ip_address *result)
|
|
|
|
{
|
|
|
|
struct in6_addr addr;
|
|
|
|
if (inet_pton(AF_INET6, input, &addr) == 1) {
|
|
|
|
result->family = AF_INET6;
|
|
|
|
memcpy(result->address.address_v6, addr.s6_addr, 16);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parse_ip(const char *ip, struct ip_address *result)
|
|
|
|
{
|
|
|
|
return parse_ipv6_address(ip, result) ||
|
|
|
|
parse_ipv4_address(ip, result);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool parse_port_number(const char *port_entry, uint16_t *port) {
|
|
|
|
char *eptr;
|
|
|
|
unsigned long port_tmp = strtoul(port_entry, &eptr, 10);
|
|
|
|
|
2024-03-18 10:18:03 -03:00
|
|
|
if (port_entry != eptr && *eptr == '\0' &&
|
|
|
|
port_tmp <= UINT16_MAX) {
|
2023-08-22 18:12:29 -03:00
|
|
|
*port = port_tmp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
bool parse_range(const char *range, uint16_t *from, uint16_t *to)
|
|
|
|
{
|
|
|
|
char *range_tmp = strdup(range);
|
|
|
|
char *dash = strchr(range_tmp, '-');
|
|
|
|
bool ret = false;
|
|
|
|
if (dash == NULL)
|
|
|
|
goto out;
|
|
|
|
*dash = '\0';
|
|
|
|
|
|
|
|
if (parse_port_number(range_tmp, from)) {
|
|
|
|
if (parse_port_number(dash + 1, to)) {
|
|
|
|
if (*from > *to) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ret = true;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(range_tmp);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-08-22 18:12:29 -03:00
|
|
|
bool network_rule::parse_port(ip_conds &entry)
|
|
|
|
{
|
|
|
|
entry.is_port = true;
|
2024-09-05 11:32:15 -03:00
|
|
|
if (parse_range(entry.sport, &entry.from_port, &entry.to_port))
|
|
|
|
return true;
|
|
|
|
if (parse_port_number(entry.sport, &entry.from_port)) {
|
|
|
|
/* if range is not used, from and to have the same value */
|
|
|
|
entry.to_port = entry.from_port;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2023-08-22 18:12:29 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool network_rule::parse_address(ip_conds &entry)
|
|
|
|
{
|
2024-04-11 19:03:43 -07:00
|
|
|
if (strcmp(entry.sip, "none") == 0) {
|
|
|
|
entry.is_none = true;
|
2024-03-14 18:23:12 -03:00
|
|
|
return true;
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
entry.is_ip = true;
|
|
|
|
return parse_ip(entry.sip, &entry.ip);
|
|
|
|
}
|
|
|
|
|
|
|
|
void network_rule::move_conditionals(struct cond_entry *conds, ip_conds &ip_cond)
|
2023-08-02 11:57:24 -03:00
|
|
|
{
|
|
|
|
struct cond_entry *cond_ent;
|
|
|
|
|
|
|
|
list_for_each(conds, cond_ent) {
|
|
|
|
/* for now disallow keyword 'in' (list) */
|
|
|
|
if (!cond_ent->eq)
|
|
|
|
yyerror("keyword \"in\" is not allowed in network rules\n");
|
2023-08-22 18:12:29 -03:00
|
|
|
if (strcmp(cond_ent->name, "ip") == 0) {
|
|
|
|
move_conditional_value("network", &ip_cond.sip, cond_ent);
|
|
|
|
if (!parse_address(ip_cond))
|
|
|
|
yyerror("network invalid ip='%s'\n", ip_cond.sip);
|
|
|
|
} else if (strcmp(cond_ent->name, "port") == 0) {
|
|
|
|
move_conditional_value("network", &ip_cond.sport, cond_ent);
|
|
|
|
if (!parse_port(ip_cond))
|
|
|
|
yyerror("network invalid port='%s'\n", ip_cond.sport);
|
|
|
|
} else {
|
|
|
|
yyerror("invalid network rule conditional \"%s\"\n",
|
|
|
|
cond_ent->name);
|
|
|
|
}
|
2023-08-02 11:57:24 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-14 17:06:12 -03:00
|
|
|
void network_rule::set_netperm(unsigned int family, unsigned int type, unsigned int protocol)
|
2014-08-24 00:00:28 -07:00
|
|
|
{
|
2023-06-26 14:39:39 -03:00
|
|
|
if (type > SOCK_PACKET) {
|
|
|
|
/* setting mask instead of a bit */
|
2024-03-14 17:06:12 -03:00
|
|
|
network_perms[family].first |= type;
|
2023-06-26 14:39:39 -03:00
|
|
|
} else
|
2024-03-14 17:06:12 -03:00
|
|
|
network_perms[family].first |= 1 << type;
|
|
|
|
network_perms[family].second |= protocol;
|
2023-06-26 14:39:39 -03:00
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
network_rule::network_rule(perm32_t perms_p, struct cond_entry *conds,
|
2023-08-22 18:12:29 -03:00
|
|
|
struct cond_entry *peer_conds):
|
2024-03-14 17:21:53 -03:00
|
|
|
dedup_perms_rule_t(AA_CLASS_NETV8), label(NULL)
|
2023-08-02 11:57:24 -03:00
|
|
|
{
|
2024-04-10 15:04:44 -03:00
|
|
|
size_t family_index, i;
|
2023-08-02 11:57:24 -03:00
|
|
|
|
2023-08-22 18:12:29 -03:00
|
|
|
move_conditionals(conds, local);
|
|
|
|
move_conditionals(peer_conds, peer);
|
2023-08-02 11:57:24 -03:00
|
|
|
free_cond_list(conds);
|
2023-08-22 18:12:29 -03:00
|
|
|
free_cond_list(peer_conds);
|
2024-01-08 16:09:13 -03:00
|
|
|
|
2024-04-10 15:04:44 -03:00
|
|
|
if (has_local_conds() || has_peer_conds()) {
|
|
|
|
const char *family[] = { "inet", "inet6" };
|
|
|
|
for (i = 0; i < sizeof(family)/sizeof(family[0]); i++) {
|
|
|
|
const struct network_tuple *mapping = NULL;
|
|
|
|
while ((mapping = net_find_mapping(mapping, family[i], NULL, NULL))) {
|
|
|
|
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
|
|
|
|
set_netperm(mapping->family, mapping->type, mapping->protocol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (family_index = AF_UNSPEC; family_index < get_af_max(); family_index++) {
|
|
|
|
network_map[family_index].push_back({ family_index, 0xFFFFFFFF, 0xFFFFFFFF });
|
|
|
|
set_netperm(family_index, 0xFFFFFFFF, 0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-01-08 16:09:13 -03:00
|
|
|
if (perms_p) {
|
|
|
|
perms = perms_p;
|
|
|
|
if (perms & ~AA_VALID_NET_PERMS)
|
|
|
|
yyerror("perms contains invalid permissions for network rules\n");
|
2023-08-22 18:12:29 -03:00
|
|
|
else if ((perms & ~AA_PEER_NET_PERMS) && has_peer_conds())
|
|
|
|
yyerror("network 'create', 'shutdown', 'setattr', 'getattr', 'bind', 'listen', 'setopt', and/or 'getopt' accesses cannot be used with peer socket conditionals\n");
|
2024-01-08 16:09:13 -03:00
|
|
|
} else {
|
|
|
|
perms = AA_VALID_NET_PERMS;
|
|
|
|
}
|
2023-08-02 11:57:24 -03:00
|
|
|
}
|
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
network_rule::network_rule(perm32_t perms_p, const char *family, const char *type,
|
2023-08-22 18:12:29 -03:00
|
|
|
const char *protocol, struct cond_entry *conds,
|
|
|
|
struct cond_entry *peer_conds):
|
2024-03-14 17:21:53 -03:00
|
|
|
dedup_perms_rule_t(AA_CLASS_NETV8), label(NULL)
|
2014-08-23 23:57:55 -07:00
|
|
|
{
|
2023-08-02 11:57:24 -03:00
|
|
|
const struct network_tuple *mapping = NULL;
|
2024-04-10 15:04:44 -03:00
|
|
|
|
|
|
|
move_conditionals(conds, local);
|
|
|
|
move_conditionals(peer_conds, peer);
|
|
|
|
free_cond_list(conds);
|
|
|
|
free_cond_list(peer_conds);
|
|
|
|
|
2023-08-02 11:57:24 -03:00
|
|
|
while ((mapping = net_find_mapping(mapping, family, type, protocol))) {
|
2024-04-10 15:04:44 -03:00
|
|
|
/* if inet conds and family are specified, fail if
|
|
|
|
* family is not af_inet or af_inet6
|
|
|
|
*/
|
|
|
|
if ((has_local_conds() || has_peer_conds()) &&
|
|
|
|
mapping->family != AF_INET && mapping->family != AF_INET6) {
|
|
|
|
yyerror("network family does not support local or peer conditionals\n");
|
|
|
|
}
|
2023-08-02 11:57:24 -03:00
|
|
|
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
|
2024-03-14 17:06:12 -03:00
|
|
|
set_netperm(mapping->family, mapping->type, mapping->protocol);
|
2023-08-02 11:57:24 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (type == NULL && network_map.empty()) {
|
|
|
|
while ((mapping = net_find_mapping(mapping, type, family, protocol))) {
|
2024-04-10 15:04:44 -03:00
|
|
|
/* if inet conds and type/protocol are
|
|
|
|
* specified, only add rules for af_inet and
|
|
|
|
* af_inet6
|
|
|
|
*/
|
|
|
|
if ((has_local_conds() || has_peer_conds()) &&
|
|
|
|
mapping->family != AF_INET && mapping->family != AF_INET6)
|
|
|
|
continue;
|
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
network_map[mapping->family].push_back({ mapping->family, mapping->type, mapping->protocol });
|
2024-03-14 17:06:12 -03:00
|
|
|
set_netperm(mapping->family, mapping->type, mapping->protocol);
|
2023-06-26 14:39:39 -03:00
|
|
|
}
|
2023-08-02 11:57:24 -03:00
|
|
|
}
|
net_find_af_name: do not assume that address families are consecutive
The network_families array is automatically built from AF_NAMES, which is
extracted from the defines in <bits/socket.h>. The code assumes that
network_families is indexed by the AF defines. However, since the
defines are sparse, and the gaps in the array are not packed with
zeroes, the array is shorter than expected, and the indexing is wrong.
When this function was written, the network families that were
covered might well have been consecutive, but this is no longer true:
there's a gap between AF_LLC (26) and AF_CAN (29). In addition,
the code that parses <sys/socket.h> does not recognise AF_DECnet (12)
due to the lower-case letters, leading to a gap betwen AF_ROSE (11)
and AF_NETBEUI (13).
This assumption caused a crash in our testing while parsing the rule
"network raw".
[smcv: split out from a larger patch, added commit message]
Reviewed-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
2015-02-27 16:20:31 +00:00
|
|
|
|
2023-08-02 11:57:24 -03:00
|
|
|
if (network_map.empty())
|
|
|
|
yyerror(_("Invalid network entry."));
|
2014-08-23 23:57:55 -07:00
|
|
|
|
2024-01-08 16:09:13 -03:00
|
|
|
if (perms_p) {
|
|
|
|
perms = perms_p;
|
|
|
|
if (perms & ~AA_VALID_NET_PERMS)
|
|
|
|
yyerror("perms contains invalid permissions for network rules\n");
|
2023-08-22 18:12:29 -03:00
|
|
|
else if ((perms & ~AA_PEER_NET_PERMS) && has_peer_conds())
|
|
|
|
yyerror("network 'create', 'shutdown', 'setattr', 'getattr', 'bind', 'listen', 'setopt', and/or 'getopt' accesses cannot be used with peer socket conditionals\n");
|
2024-01-08 16:09:13 -03:00
|
|
|
} else {
|
|
|
|
perms = AA_VALID_NET_PERMS;
|
|
|
|
}
|
2023-06-26 14:39:39 -03:00
|
|
|
}
|
net_find_af_name: do not assume that address families are consecutive
The network_families array is automatically built from AF_NAMES, which is
extracted from the defines in <bits/socket.h>. The code assumes that
network_families is indexed by the AF defines. However, since the
defines are sparse, and the gaps in the array are not packed with
zeroes, the array is shorter than expected, and the indexing is wrong.
When this function was written, the network families that were
covered might well have been consecutive, but this is no longer true:
there's a gap between AF_LLC (26) and AF_CAN (29). In addition,
the code that parses <sys/socket.h> does not recognise AF_DECnet (12)
due to the lower-case letters, leading to a gap betwen AF_ROSE (11)
and AF_NETBEUI (13).
This assumption caused a crash in our testing while parsing the rule
"network raw".
[smcv: split out from a larger patch, added commit message]
Reviewed-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
2015-02-27 16:20:31 +00:00
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
network_rule::network_rule(perm32_t perms_p, unsigned int family, unsigned int type):
|
2024-03-14 17:21:53 -03:00
|
|
|
dedup_perms_rule_t(AA_CLASS_NETV8), label(NULL)
|
2023-06-26 14:39:39 -03:00
|
|
|
{
|
|
|
|
network_map[family].push_back({ family, type, 0xFFFFFFFF });
|
2024-03-14 17:06:12 -03:00
|
|
|
set_netperm(family, type, 0xFFFFFFFF);
|
2024-01-08 16:09:13 -03:00
|
|
|
|
|
|
|
if (perms_p) {
|
|
|
|
perms = perms_p;
|
|
|
|
if (perms & ~AA_VALID_NET_PERMS)
|
|
|
|
yyerror("perms contains invalid permissions for network rules\n");
|
2023-08-22 18:12:29 -03:00
|
|
|
else if ((perms & ~AA_PEER_NET_PERMS) && has_peer_conds())
|
|
|
|
yyerror("network 'create', 'shutdown', 'setattr', 'getattr', 'bind', 'listen', 'setopt', and/or 'getopt' accesses cannot be used with peer socket conditionals\n");
|
2024-01-08 16:09:13 -03:00
|
|
|
} else {
|
|
|
|
perms = AA_VALID_NET_PERMS;
|
|
|
|
}
|
2014-08-23 23:57:55 -07:00
|
|
|
}
|
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
ostream &network_rule::dump(ostream &os)
|
2014-08-23 23:55:12 -07:00
|
|
|
{
|
2023-06-26 14:39:39 -03:00
|
|
|
class_rule_t::dump(os);
|
|
|
|
|
2014-08-23 23:55:12 -07:00
|
|
|
unsigned int count = sizeof(sock_types)/sizeof(sock_types[0]);
|
|
|
|
unsigned int mask = ~((1 << count) -1);
|
2023-06-26 14:39:39 -03:00
|
|
|
unsigned int j;
|
|
|
|
|
|
|
|
/* This can only be set by an unqualified network rule */
|
|
|
|
if (network_map.find(AF_UNSPEC) != network_map.end()) {
|
|
|
|
os << ",\n";
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto& perm : network_perms) {
|
|
|
|
unsigned int family = perm.first;
|
2024-03-14 17:06:12 -03:00
|
|
|
unsigned int type = perm.second.first;
|
|
|
|
unsigned int protocol = perm.second.second;
|
2023-06-26 14:39:39 -03:00
|
|
|
|
|
|
|
const char *family_name = net_find_af_name(family);
|
|
|
|
if (family_name)
|
|
|
|
os << " " << family_name;
|
|
|
|
else
|
|
|
|
os << " #" << family;
|
|
|
|
|
|
|
|
/* All types/protocols */
|
|
|
|
if (type == 0xffffffff || type == ALL_TYPES)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
printf(" {");
|
|
|
|
|
|
|
|
for (j = 0; j < count; j++) {
|
|
|
|
const char *type_name;
|
|
|
|
if (type & (1 << j)) {
|
|
|
|
type_name = sock_types[j].name;
|
|
|
|
if (type_name)
|
|
|
|
os << " " << type_name;
|
|
|
|
else
|
|
|
|
os << " #" << j;
|
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
}
|
2023-06-26 14:39:39 -03:00
|
|
|
if (type & mask)
|
|
|
|
os << " #" << std::hex << (type & mask);
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
printf(" }");
|
2024-03-14 17:06:12 -03:00
|
|
|
|
|
|
|
const char *protocol_name = net_find_protocol_name(protocol);
|
|
|
|
if (protocol_name)
|
|
|
|
os << " " << protocol_name;
|
|
|
|
else
|
|
|
|
os << " #" << protocol;
|
2023-06-26 14:39:39 -03:00
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
os << ",\n";
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int network_rule::expand_variables(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void network_rule::warn_once(const char *name)
|
|
|
|
{
|
|
|
|
rule_t::warn_once(name, "network rules not enforced");
|
|
|
|
}
|
|
|
|
|
2023-08-22 18:12:29 -03:00
|
|
|
std::string gen_ip_cond(const struct ip_address ip)
|
|
|
|
{
|
|
|
|
std::ostringstream oss;
|
|
|
|
int i;
|
|
|
|
if (ip.family == AF_INET) {
|
|
|
|
/* add a byte containing the size of the following ip */
|
2024-03-14 17:17:23 -03:00
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << IPV4_SIZE;
|
2023-08-22 18:12:29 -03:00
|
|
|
|
|
|
|
u8 *byte = (u8 *) &ip.address.address_v4; /* in network byte order */
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned int>(byte[i]);
|
|
|
|
} else {
|
|
|
|
/* add a byte containing the size of the following ip */
|
2024-03-14 17:17:23 -03:00
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << IPV6_SIZE;
|
2023-08-22 18:12:29 -03:00
|
|
|
for (i = 0; i < 16; ++i)
|
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned int>(ip.address.address_v6[i]);
|
|
|
|
}
|
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string gen_port_cond(uint16_t port)
|
|
|
|
{
|
|
|
|
std::ostringstream oss;
|
|
|
|
if (port > 0) {
|
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((port & 0xff00) >> 8);
|
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (port & 0xff);
|
|
|
|
} else {
|
|
|
|
oss << "..";
|
|
|
|
}
|
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
2024-03-14 17:31:52 -03:00
|
|
|
std::list<std::ostringstream> gen_all_ip_options(std::ostringstream &oss) {
|
|
|
|
|
|
|
|
std::list<std::ostringstream> all_streams;
|
2024-04-11 19:03:43 -07:00
|
|
|
std::ostringstream none, ipv4, ipv6;
|
2024-03-14 17:31:52 -03:00
|
|
|
int i;
|
2024-04-11 19:03:43 -07:00
|
|
|
none << oss.str();
|
2024-03-14 17:31:52 -03:00
|
|
|
ipv4 << oss.str();
|
|
|
|
ipv6 << oss.str();
|
|
|
|
|
2024-04-11 19:03:43 -07:00
|
|
|
none << "\\x" << std::setfill('0') << std::setw(2) << std::hex << NONE_SIZE;
|
2024-03-14 17:31:52 -03:00
|
|
|
|
|
|
|
/* add a byte containing the size of the following ip */
|
|
|
|
ipv4 << "\\x" << std::setfill('0') << std::setw(2) << std::hex << IPV4_SIZE;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
ipv4 << ".";
|
|
|
|
|
|
|
|
/* add a byte containing the size of the following ip */
|
|
|
|
ipv6 << "\\x" << std::setfill('0') << std::setw(2) << std::hex << IPV6_SIZE;
|
|
|
|
for (i = 0; i < 16; ++i)
|
|
|
|
ipv6 << ".";
|
|
|
|
|
2024-04-11 19:03:43 -07:00
|
|
|
all_streams.push_back(std::move(none));
|
2024-03-14 17:31:52 -03:00
|
|
|
all_streams.push_back(std::move(ipv4));
|
|
|
|
all_streams.push_back(std::move(ipv6));
|
|
|
|
|
|
|
|
return all_streams;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::list<std::ostringstream> copy_streams_list(std::list<std::ostringstream> &streams)
|
2023-08-22 18:12:29 -03:00
|
|
|
{
|
2024-03-14 17:31:52 -03:00
|
|
|
std::list<std::ostringstream> streams_copy;
|
|
|
|
for (auto &oss : streams) {
|
|
|
|
std::ostringstream oss_copy(oss.str());
|
|
|
|
streams_copy.push_back(std::move(oss_copy));
|
|
|
|
}
|
|
|
|
return streams_copy;
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
bool network_rule::gen_ip_conds(Profile &prof, std::list<std::ostringstream> &streams, ip_conds &entry, bool is_peer, uint16_t port, bool is_port, bool is_cmd)
|
2024-03-14 17:31:52 -03:00
|
|
|
{
|
|
|
|
std::string buf;
|
2023-08-02 02:07:36 -07:00
|
|
|
perm32_t cond_perms;
|
2024-03-14 17:31:52 -03:00
|
|
|
std::list<std::ostringstream> ip_streams;
|
|
|
|
|
|
|
|
for (auto &oss : streams) {
|
2024-09-05 11:32:15 -03:00
|
|
|
if (is_port && !(entry.is_ip && entry.is_none)) {
|
2024-03-14 17:31:52 -03:00
|
|
|
/* encode port type (privileged - 1, remote - 2, unprivileged - 0) */
|
2024-09-05 11:32:15 -03:00
|
|
|
if (!is_peer && perms & AA_NET_BIND && port < IPPORT_RESERVED)
|
2024-03-14 17:31:52 -03:00
|
|
|
oss << "\\x01";
|
|
|
|
else if (is_peer)
|
|
|
|
oss << "\\x02";
|
|
|
|
else
|
|
|
|
oss << "\\x00";
|
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
oss << gen_port_cond(port);
|
2024-03-14 17:31:52 -03:00
|
|
|
} else {
|
|
|
|
/* port type + port number */
|
|
|
|
oss << "...";
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
}
|
|
|
|
|
2024-03-14 17:31:52 -03:00
|
|
|
ip_streams = std::move(streams);
|
|
|
|
streams.clear();
|
|
|
|
|
|
|
|
for (auto &oss : ip_streams) {
|
|
|
|
if (entry.is_ip) {
|
|
|
|
oss << gen_ip_cond(entry.ip);
|
|
|
|
streams.push_back(std::move(oss));
|
2024-04-11 19:03:43 -07:00
|
|
|
} else if (entry.is_none) {
|
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << NONE_SIZE;
|
2024-03-14 18:23:12 -03:00
|
|
|
streams.push_back(std::move(oss));
|
2024-03-14 17:31:52 -03:00
|
|
|
} else {
|
|
|
|
streams.splice(streams.end(), gen_all_ip_options(oss));
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
}
|
|
|
|
|
2024-03-14 17:31:52 -03:00
|
|
|
cond_perms = map_perms(perms);
|
|
|
|
if (!is_cmd && (label || is_peer))
|
|
|
|
cond_perms = (AA_CONT_MATCH << 1);
|
|
|
|
|
|
|
|
for (auto &oss : streams) {
|
|
|
|
oss << "\\x00"; /* null transition */
|
|
|
|
|
|
|
|
buf = oss.str();
|
|
|
|
/* AA_CONT_MATCH mapping (cond_perms) only applies to perms, not audit */
|
2024-05-10 03:06:22 -07:00
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, cond_perms,
|
2024-03-14 17:31:52 -03:00
|
|
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
|
|
|
parseopts))
|
|
|
|
return false;
|
|
|
|
|
2024-03-14 18:23:12 -03:00
|
|
|
if (label || is_peer) {
|
|
|
|
if (!is_peer)
|
|
|
|
cond_perms = map_perms(perms);
|
2024-03-14 17:31:52 -03:00
|
|
|
|
|
|
|
oss << default_match_pattern; /* label - not used for now */
|
|
|
|
oss << "\\x00"; /* null transition */
|
|
|
|
|
|
|
|
buf = oss.str();
|
2024-05-10 03:06:22 -07:00
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, cond_perms,
|
2024-03-14 17:31:52 -03:00
|
|
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
|
|
|
parseopts))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2023-08-22 18:12:29 -03:00
|
|
|
}
|
|
|
|
|
2024-03-14 17:06:12 -03:00
|
|
|
bool network_rule::gen_net_rule(Profile &prof, u16 family, unsigned int type_mask, unsigned int protocol) {
|
2023-06-26 14:39:39 -03:00
|
|
|
std::ostringstream buffer;
|
|
|
|
std::string buf;
|
|
|
|
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_NETV8;
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((family & 0xff00) >> 8);
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (family & 0xff);
|
|
|
|
if (type_mask > 0xffff) {
|
|
|
|
buffer << "..";
|
|
|
|
} else {
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((type_mask & 0xff00) >> 8);
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (type_mask & 0xff);
|
2014-08-23 23:55:12 -07:00
|
|
|
}
|
|
|
|
|
2024-03-06 10:04:04 -03:00
|
|
|
if (!features_supports_inet || (family != AF_INET && family != AF_INET6)) {
|
2024-02-29 17:30:38 -03:00
|
|
|
buf = buffer.str();
|
2024-05-10 03:06:22 -07:00
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, map_perms(perms),
|
|
|
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
|
|
|
parseopts))
|
2024-02-29 17:30:38 -03:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-03-14 17:24:36 -03:00
|
|
|
buf = buffer.str();
|
|
|
|
/* create perms need to be generated excluding the rest of the perms */
|
|
|
|
if (perms & AA_NET_CREATE) {
|
2024-05-10 03:06:22 -07:00
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, map_perms(perms & AA_NET_CREATE) | (AA_CONT_MATCH << 1),
|
2024-03-14 17:24:36 -03:00
|
|
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms & AA_NET_CREATE) : 0,
|
|
|
|
parseopts))
|
|
|
|
return false;
|
|
|
|
}
|
2024-03-14 17:06:12 -03:00
|
|
|
|
|
|
|
/* encode protocol */
|
|
|
|
if (protocol > 0xffff) {
|
|
|
|
buffer << "..";
|
|
|
|
} else {
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << ((protocol & 0xff00) >> 8);
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << (protocol & 0xff);
|
|
|
|
}
|
|
|
|
|
2023-08-22 18:12:29 -03:00
|
|
|
if (perms & AA_PEER_NET_PERMS) {
|
2024-09-05 11:32:15 -03:00
|
|
|
for (int peer_port = peer.from_port; peer_port <= peer.to_port; peer_port++) {
|
|
|
|
std::list<std::ostringstream> streams;
|
|
|
|
std::ostringstream cmd_buffer;
|
2024-03-14 17:31:52 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
cmd_buffer << buffer.str();
|
|
|
|
streams.push_back(std::move(cmd_buffer));
|
2023-08-22 18:12:29 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
if (!gen_ip_conds(prof, streams, peer, true, peer_port, peer.is_port, false))
|
|
|
|
return false;
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
for (auto &oss : streams) {
|
|
|
|
oss << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_ADDR;
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
for (int local_port = local.from_port; local_port <= local.to_port; local_port++) {
|
|
|
|
std::list<std::ostringstream> localstreams;
|
2023-08-22 18:12:29 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
for (auto &oss : streams) {
|
|
|
|
/* we need to copy streams because each local_port should be an unique entry */
|
|
|
|
std::ostringstream local_buffer;
|
|
|
|
local_buffer << oss.str();
|
|
|
|
localstreams.push_back(std::move(local_buffer));
|
|
|
|
}
|
2024-03-14 17:31:52 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
if (!gen_ip_conds(prof, localstreams, local, false, local_port, local.is_port, true))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-14 17:31:52 -03:00
|
|
|
|
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
for (int local_port = local.from_port; local_port <= local.to_port; local_port++) {
|
|
|
|
std::list<std::ostringstream> streams;
|
|
|
|
std::ostringstream common_buffer;
|
2024-03-14 17:31:52 -03:00
|
|
|
|
2024-09-05 11:32:15 -03:00
|
|
|
common_buffer << buffer.str();
|
|
|
|
streams.push_back(std::move(common_buffer));
|
|
|
|
if (!gen_ip_conds(prof, streams, local, false, local_port, local.is_port, false))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (perms & AA_NET_LISTEN) {
|
|
|
|
std::list<std::ostringstream> cmd_streams;
|
|
|
|
cmd_streams = copy_streams_list(streams);
|
|
|
|
|
|
|
|
for (auto &cmd_buffer : streams) {
|
|
|
|
std::ostringstream listen_buffer;
|
|
|
|
listen_buffer << cmd_buffer.str();
|
|
|
|
listen_buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_LISTEN;
|
|
|
|
/* length of queue allowed - not used for now */
|
|
|
|
listen_buffer << "..";
|
|
|
|
buf = listen_buffer.str();
|
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, map_perms(perms),
|
|
|
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
|
|
|
parseopts))
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
}
|
2024-09-05 11:32:15 -03:00
|
|
|
if (perms & AA_NET_OPT) {
|
|
|
|
std::list<std::ostringstream> cmd_streams;
|
|
|
|
cmd_streams = copy_streams_list(streams);
|
|
|
|
|
|
|
|
for (auto &cmd_buffer : streams) {
|
|
|
|
std::ostringstream opt_buffer;
|
|
|
|
opt_buffer << cmd_buffer.str();
|
|
|
|
opt_buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << CMD_OPT;
|
|
|
|
/* level - not used for now */
|
|
|
|
opt_buffer << "..";
|
|
|
|
/* socket mapping - not used for now */
|
|
|
|
opt_buffer << "..";
|
|
|
|
buf = opt_buffer.str();
|
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, map_perms(perms),
|
|
|
|
dedup_perms_rule_t::audit == AUDIT_FORCE ? map_perms(perms) : 0,
|
|
|
|
parseopts))
|
|
|
|
return false;
|
|
|
|
}
|
2023-08-22 18:12:29 -03:00
|
|
|
}
|
|
|
|
}
|
2024-03-14 17:31:52 -03:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
return true;
|
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
int network_rule::gen_policy_re(Profile &prof)
|
|
|
|
{
|
|
|
|
std::ostringstream buffer;
|
|
|
|
std::string buf;
|
|
|
|
|
|
|
|
if (!features_supports_networkv8) {
|
|
|
|
warn_once(prof.name);
|
|
|
|
return RULE_NOT_SUPPORTED;
|
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
for (const auto& perm : network_perms) {
|
|
|
|
unsigned int family = perm.first;
|
2024-03-14 17:06:12 -03:00
|
|
|
unsigned int type = perm.second.first;
|
|
|
|
unsigned int protocol = perm.second.second;
|
2023-06-26 14:39:39 -03:00
|
|
|
|
|
|
|
if (type > 0xffff) {
|
2024-03-14 17:06:12 -03:00
|
|
|
if (!gen_net_rule(prof, family, type, protocol))
|
2023-06-26 14:39:39 -03:00
|
|
|
goto fail;
|
|
|
|
} else {
|
|
|
|
int t;
|
|
|
|
/* generate rules for types that are set */
|
|
|
|
for (t = 0; t < 16; t++) {
|
|
|
|
if (type & (1 << t)) {
|
2024-03-14 17:06:12 -03:00
|
|
|
if (!gen_net_rule(prof, family, t, protocol))
|
2023-06-26 14:39:39 -03:00
|
|
|
goto fail;
|
2014-08-23 23:55:12 -07:00
|
|
|
}
|
|
|
|
}
|
2023-06-26 14:39:39 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return RULE_OK;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return RULE_ERROR;
|
|
|
|
|
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
|
2023-06-26 14:39:39 -03:00
|
|
|
/* initialize static members */
|
|
|
|
unsigned int *network_rule::allow = NULL;
|
|
|
|
unsigned int *network_rule::audit = NULL;
|
|
|
|
unsigned int *network_rule::deny = NULL;
|
|
|
|
unsigned int *network_rule::quiet = NULL;
|
|
|
|
|
|
|
|
bool network_rule::alloc_net_table()
|
|
|
|
{
|
|
|
|
if (allow)
|
|
|
|
return true;
|
|
|
|
allow = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
|
|
|
audit = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
|
|
|
deny = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
|
|
|
quiet = (unsigned int *) calloc(get_af_max(), sizeof(unsigned int));
|
|
|
|
if (!allow || !audit || !deny || !quiet)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update is required because at the point of the creation of the
|
|
|
|
* network_rule object, we don't have owner, rule_mode, or audit
|
|
|
|
* set.
|
|
|
|
*/
|
|
|
|
void network_rule::update_compat_net(void)
|
|
|
|
{
|
|
|
|
if (!alloc_net_table())
|
|
|
|
yyerror(_("Memory allocation error."));
|
|
|
|
|
|
|
|
for (auto& nm: network_map) {
|
|
|
|
for (auto& entry : nm.second) {
|
|
|
|
if (entry.type > SOCK_PACKET) {
|
|
|
|
/* setting mask instead of a bit */
|
|
|
|
if (rule_mode == RULE_DENY) {
|
|
|
|
deny[entry.family] |= entry.type;
|
|
|
|
if (dedup_perms_rule_t::audit != AUDIT_FORCE)
|
|
|
|
quiet[entry.family] |= entry.type;
|
|
|
|
} else {
|
|
|
|
allow[entry.family] |= entry.type;
|
|
|
|
if (dedup_perms_rule_t::audit == AUDIT_FORCE)
|
|
|
|
audit[entry.family] |= entry.type;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (rule_mode == RULE_DENY) {
|
|
|
|
deny[entry.family] |= 1 << entry.type;
|
|
|
|
if (dedup_perms_rule_t::audit != AUDIT_FORCE)
|
|
|
|
quiet[entry.family] |= 1 << entry.type;
|
|
|
|
} else {
|
|
|
|
allow[entry.family] |= 1 << entry.type;
|
|
|
|
if (dedup_perms_rule_t::audit == AUDIT_FORCE)
|
|
|
|
audit[entry.family] |= 1 << entry.type;
|
|
|
|
}
|
|
|
|
}
|
2014-08-23 23:55:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-02 17:58:38 -03:00
|
|
|
|
2024-03-14 20:34:14 -03:00
|
|
|
static int cmp_ip_conds(ip_conds const &lhs, ip_conds const &rhs)
|
|
|
|
{
|
|
|
|
int res = null_strcmp(lhs.sip, rhs.sip);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
res = null_strcmp(lhs.sport, rhs.sport);
|
|
|
|
if (res)
|
|
|
|
return res;
|
2024-04-11 19:03:43 -07:00
|
|
|
return lhs.is_none - rhs.is_none;
|
2024-03-14 20:34:14 -03:00
|
|
|
}
|
|
|
|
|
2024-03-14 17:06:12 -03:00
|
|
|
static int cmp_network_map(std::unordered_map<unsigned int, std::pair<unsigned int, unsigned int>> lhs,
|
|
|
|
std::unordered_map<unsigned int, std::pair<unsigned int, unsigned int>> rhs)
|
2023-08-02 17:58:38 -03:00
|
|
|
{
|
|
|
|
int res;
|
|
|
|
size_t family_index;
|
|
|
|
for (family_index = AF_UNSPEC; family_index < get_af_max(); family_index++) {
|
2024-03-14 17:06:12 -03:00
|
|
|
res = lhs[family_index].first - rhs[family_index].first;
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
res = lhs[family_index].second - rhs[family_index].second;
|
2023-08-02 17:58:38 -03:00
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int network_rule::cmp(rule_t const &rhs) const
|
|
|
|
{
|
|
|
|
int res = dedup_perms_rule_t::cmp(rhs);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
network_rule const &nrhs = rule_cast<network_rule const &>(rhs);
|
2024-03-14 20:34:14 -03:00
|
|
|
res = cmp_network_map(network_perms, nrhs.network_perms);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
res = cmp_ip_conds(local, nrhs.local);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
res = cmp_ip_conds(peer, nrhs.peer);
|
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
return null_strcmp(label, nrhs.label);
|
2023-08-02 17:58:38 -03:00
|
|
|
};
|