forked from Minki/linux
e3e0b582c3
Remove initial SIDs that have never been used or are no longer used by the kernel from its string table, which is also used to generate the SECINITSID_* symbols referenced in code. Update the code to gracefully handle the fact that these can now be NULL. Stop treating it as an error if a policy defines additional initial SIDs unknown to the kernel. Do not load unused initial SID contexts into the sidtab. Fix the incorrect usage of the name from the ocontext in error messages when loading initial SIDs since these are not presently written to the kernel policy and are therefore always NULL. After this change, it is possible to safely reclaim and reuse some of the unused initial SIDs without compatibility issues. Specifically, unused initial SIDs that were being assigned the same context as the unlabeled initial SID in policies can be reclaimed and reused for another purpose, with existing policies still treating them as having the unlabeled context and future policies having the option of mapping them to a more specific context. For example, this could have been used when the infiniband labeling support was introduced to define initial SIDs for the default pkey and endport SIDs similar to the handling of port/netif/node SIDs rather than always using SECINITSID_UNLABELED as the default. The set of safely reclaimable unused initial SIDs across all known policies is igmp_packet (13), icmp_socket (14), tcp_socket (15), kmod (24), policy (25), and scmp_packet (26); these initial SIDs were assigned the same context as unlabeled in all known policies including mls. If only considering non-mls policies (i.e. assuming that mls users always upgrade policy with their kernels), the set of safely reclaimable unused initial SIDs further includes file_labels (6), init (7), sysctl_modprobe (16), and sysctl_fs (18) through sysctl_dev (23). Adding new initial SIDs beyond SECINITSID_NUM to policy unfortunately became a fatal error in commit24ed7fdae6
("selinux: use separate table for initial SID lookup") and even before that it could cause problems on a policy reload (collision between the new initial SID and one allocated at runtime) ever since commit42596eafdd
("selinux: load the initial SIDs upon every policy load") so we cannot safely start adding new initial SIDs to policies beyond SECINITSID_NUM (27) until such a time as all such kernels do not need to be supported and only those that include this commit are relevant. That is not a big deal since we haven't added a new initial SID since 2004 (v2.6.7) and we have plenty of unused ones we can reclaim if we truly need one. If we want to avoid the wasted storage in initial_sid_to_string[] and/or sidtab->isids[] for the unused initial SIDs, we could introduce an indirection between the kernel initial SID values and the policy initial SID values and just map the policy SID values in the ocontexts to the kernel values during policy_load_isids(). Originally I thought we'd do this by preserving the initial SID names in the kernel policy and creating a mapping at load time like we do for the security classes and permissions but that would require a new kernel policy format version and associated changes to libsepol/checkpolicy and I'm not sure it is justified. Simpler approach is just to create a fixed mapping table in the kernel from the existing fixed policy values to the kernel values. Less flexible but probably sufficient. A separate selinux userspace change was applied in8677ce5e8f
to enable removal of most of the unused initial SID contexts from policies, but there is no dependency between that change and this one. That change permits removing all of the unused initial SID contexts from policy except for the fs and sysctl SID contexts. The initial SID declarations themselves would remain in policy to preserve the values of subsequent ones but the contexts can be dropped. If/when the kernel decides to reuse one of them, future policies can change the name and start assigning a context again without breaking compatibility. Here is how I would envision staging changes to the initial SIDs in a compatible manner after this commit is applied: 1. At any time after this commit is applied, the kernel could choose to reclaim one of the safely reclaimable unused initial SIDs listed above for a new purpose (i.e. replace its NULL entry in the initial_sid_to_string[] table with a new name and start using the newly generated SECINITSID_name symbol in code), and refpolicy could at that time rename its declaration of that initial SID to reflect its new purpose and start assigning it a context going forward. Existing/old policies would map the reclaimed initial SID to the unlabeled context, so that would be the initial default behavior until policies are updated. This doesn't depend on the selinux userspace change; it will work with existing policies and userspace. 2. In 6 months or so we'll have another SELinux userspace release that will include the libsepol/checkpolicy support for omitting unused initial SID contexts. 3. At any time after that release, refpolicy can make that release its minimum build requirement and drop the sid context statements (but not the sid declarations) for all of the unused initial SIDs except for fs and sysctl, which must remain for compatibility on policy reload with old kernels and for compatibility with kernels that were still using SECINITSID_SYSCTL (< 2.6.39). This doesn't depend on this kernel commit; it will work with previous kernels as well. 4. After N years for some value of N, refpolicy decides that it no longer cares about policy reload compatibility for kernels that predate this kernel commit, and refpolicy drops the fs and sysctl SID contexts from policy too (but retains the declarations). 5. After M years for some value of M, the kernel decides that it no longer cares about compatibility with refpolicies that predate step 4 (dropping the fs and sysctl SIDs), and those two SIDs also become safely reclaimable. This step is optional and need not ever occur unless we decide that the need to reclaim those two SIDs outweighs the compatibility cost. 6. After O years for some value of O, refpolicy decides that it no longer cares about policy load (not just reload) compatibility for kernels that predate this kernel commit, and both kernel and refpolicy can then start adding and using new initial SIDs beyond 27. This does not depend on the previous change (step 5) and can occur independent of it. Fixes: https://github.com/SELinuxProject/selinux-kernel/issues/12 Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: Paul Moore <paul@paul-moore.com>
143 lines
3.5 KiB
C
143 lines
3.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/* NOTE: we really do want to use the kernel headers here */
|
|
#define __EXPORTED_HEADERS__
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
|
|
struct security_class_mapping {
|
|
const char *name;
|
|
const char *perms[sizeof(unsigned) * 8 + 1];
|
|
};
|
|
|
|
#include "classmap.h"
|
|
#include "initial_sid_to_string.h"
|
|
|
|
const char *progname;
|
|
|
|
static void usage(void)
|
|
{
|
|
printf("usage: %s flask.h av_permissions.h\n", progname);
|
|
exit(1);
|
|
}
|
|
|
|
static char *stoupperx(const char *s)
|
|
{
|
|
char *s2 = strdup(s);
|
|
char *p;
|
|
|
|
if (!s2) {
|
|
fprintf(stderr, "%s: out of memory\n", progname);
|
|
exit(3);
|
|
}
|
|
|
|
for (p = s2; *p; p++)
|
|
*p = toupper(*p);
|
|
return s2;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i, j;
|
|
int isids_len;
|
|
FILE *fout;
|
|
|
|
progname = argv[0];
|
|
|
|
if (argc < 3)
|
|
usage();
|
|
|
|
fout = fopen(argv[1], "w");
|
|
if (!fout) {
|
|
fprintf(stderr, "Could not open %s for writing: %s\n",
|
|
argv[1], strerror(errno));
|
|
exit(2);
|
|
}
|
|
|
|
for (i = 0; secclass_map[i].name; i++) {
|
|
struct security_class_mapping *map = &secclass_map[i];
|
|
map->name = stoupperx(map->name);
|
|
for (j = 0; map->perms[j]; j++)
|
|
map->perms[j] = stoupperx(map->perms[j]);
|
|
}
|
|
|
|
isids_len = sizeof(initial_sid_to_string) / sizeof (char *);
|
|
for (i = 1; i < isids_len; i++) {
|
|
const char *s = initial_sid_to_string[i];
|
|
|
|
if (s)
|
|
initial_sid_to_string[i] = stoupperx(s);
|
|
}
|
|
|
|
fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
|
|
fprintf(fout, "#ifndef _SELINUX_FLASK_H_\n#define _SELINUX_FLASK_H_\n\n");
|
|
|
|
for (i = 0; secclass_map[i].name; i++) {
|
|
struct security_class_mapping *map = &secclass_map[i];
|
|
fprintf(fout, "#define SECCLASS_%-39s %2d\n", map->name, i+1);
|
|
}
|
|
|
|
fprintf(fout, "\n");
|
|
|
|
for (i = 1; i < isids_len; i++) {
|
|
const char *s = initial_sid_to_string[i];
|
|
if (s)
|
|
fprintf(fout, "#define SECINITSID_%-39s %2d\n", s, i);
|
|
}
|
|
fprintf(fout, "\n#define SECINITSID_NUM %d\n", i-1);
|
|
fprintf(fout, "\nstatic inline bool security_is_socket_class(u16 kern_tclass)\n");
|
|
fprintf(fout, "{\n");
|
|
fprintf(fout, "\tbool sock = false;\n\n");
|
|
fprintf(fout, "\tswitch (kern_tclass) {\n");
|
|
for (i = 0; secclass_map[i].name; i++) {
|
|
static char s[] = "SOCKET";
|
|
struct security_class_mapping *map = &secclass_map[i];
|
|
int len = strlen(map->name), l = sizeof(s) - 1;
|
|
if (len >= l && memcmp(map->name + len - l, s, l) == 0)
|
|
fprintf(fout, "\tcase SECCLASS_%s:\n", map->name);
|
|
}
|
|
fprintf(fout, "\t\tsock = true;\n");
|
|
fprintf(fout, "\t\tbreak;\n");
|
|
fprintf(fout, "\tdefault:\n");
|
|
fprintf(fout, "\t\tbreak;\n");
|
|
fprintf(fout, "\t}\n\n");
|
|
fprintf(fout, "\treturn sock;\n");
|
|
fprintf(fout, "}\n");
|
|
|
|
fprintf(fout, "\n#endif\n");
|
|
fclose(fout);
|
|
|
|
fout = fopen(argv[2], "w");
|
|
if (!fout) {
|
|
fprintf(stderr, "Could not open %s for writing: %s\n",
|
|
argv[2], strerror(errno));
|
|
exit(4);
|
|
}
|
|
|
|
fprintf(fout, "/* This file is automatically generated. Do not edit. */\n");
|
|
fprintf(fout, "#ifndef _SELINUX_AV_PERMISSIONS_H_\n#define _SELINUX_AV_PERMISSIONS_H_\n\n");
|
|
|
|
for (i = 0; secclass_map[i].name; i++) {
|
|
struct security_class_mapping *map = &secclass_map[i];
|
|
int len = strlen(map->name);
|
|
for (j = 0; map->perms[j]; j++) {
|
|
if (j >= 32) {
|
|
fprintf(stderr, "Too many permissions to fit into an access vector at (%s, %s).\n",
|
|
map->name, map->perms[j]);
|
|
exit(5);
|
|
}
|
|
fprintf(fout, "#define %s__%-*s 0x%08xU\n", map->name,
|
|
39-len, map->perms[j], 1U<<j);
|
|
}
|
|
}
|
|
|
|
fprintf(fout, "\n#endif\n");
|
|
fclose(fout);
|
|
exit(0);
|
|
}
|