Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  security.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Security plug functions
 *
 * Copyright (C) 2001 WireX Communications, Inc <chris@wirex.com>
 * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (C) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
 * Copyright (C) 2016 Mellanox Technologies
 * Copyright (C) 2023 Microsoft Corporation <paul@paul-moore.com>
 */


#define pr_fmt(fmt) "LSM: " fmt

#include <linux/bpf.h>
#include <linux/capability.h>
#include <linux/dcache.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kernel_read_file.h>
#include <linux/lsm_hooks.h>
#include <linux/mman.h>
#include <linux/mount.h>
#include <linux/personality.h>
#include <linux/backing-dev.h>
#include <linux/string.h>
#include <linux/xattr.h>
#include <linux/msg.h>
#include <linux/overflow.h>
#include <linux/perf_event.h>
#include <linux/fs.h>
#include <net/flow.h>
#include <net/sock.h>

#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX

/*
 * Identifier for the LSM static calls.
 * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
 * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
 */

#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX

/*
 * Call the macro M for each LSM hook MAX_LSM_COUNT times.
 */

#define LSM_LOOP_UNROLL(M, ...)   \
do {      \
 UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
while (0)

#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)

/*
 * These are descriptions of the reasons that can be passed to the
 * security_locked_down() LSM hook. Placing this array here allows
 * all security modules to use the same descriptions for auditing
 * purposes.
 */

const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
 [LOCKDOWN_NONE] = "none",
 [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
 [LOCKDOWN_DEV_MEM] = "/dev/mem,kmem,port",
 [LOCKDOWN_EFI_TEST] = "/dev/efi_test access",
 [LOCKDOWN_KEXEC] = "kexec of unsigned images",
 [LOCKDOWN_HIBERNATION] = "hibernation",
 [LOCKDOWN_PCI_ACCESS] = "direct PCI access",
 [LOCKDOWN_IOPORT] = "raw io port access",
 [LOCKDOWN_MSR] = "raw MSR access",
 [LOCKDOWN_ACPI_TABLES] = "modifying ACPI tables",
 [LOCKDOWN_DEVICE_TREE] = "modifying device tree contents",
 [LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
 [LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
 [LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",
 [LOCKDOWN_MMIOTRACE] = "unsafe mmio",
 [LOCKDOWN_DEBUGFS] = "debugfs access",
 [LOCKDOWN_XMON_WR] = "xmon write access",
 [LOCKDOWN_BPF_WRITE_USER] = "use of bpf to write user RAM",
 [LOCKDOWN_DBG_WRITE_KERNEL] = "use of kgdb/kdb to write kernel RAM",
 [LOCKDOWN_RTAS_ERROR_INJECTION] = "RTAS error injection",
 [LOCKDOWN_INTEGRITY_MAX] = "integrity",
 [LOCKDOWN_KCORE] = "/proc/kcore access",
 [LOCKDOWN_KPROBES] = "use of kprobes",
 [LOCKDOWN_BPF_READ_KERNEL] = "use of bpf to read kernel RAM",
 [LOCKDOWN_DBG_READ_KERNEL] = "use of kgdb/kdb to read kernel RAM",
 [LOCKDOWN_PERF] = "unsafe use of perf",
 [LOCKDOWN_TRACEFS] = "use of tracefs",
 [LOCKDOWN_XMON_RW] = "xmon read and write access",
 [LOCKDOWN_XFRM_SECRET] = "xfrm SA secret",
 [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
};

static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);

static struct kmem_cache *lsm_file_cache;
static struct kmem_cache *lsm_inode_cache;

char *lsm_names;
static struct lsm_blob_sizes blob_sizes __ro_after_init;

/* Boot-time LSM user choice */
static __initdata const char *chosen_lsm_order;
static __initdata const char *chosen_major_lsm;

static __initconst const char *const builtin_lsm_order = CONFIG_LSM;

/* Ordered list of LSMs to initialize. */
static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
static __initdata struct lsm_info *exclusive;

#ifdef CONFIG_HAVE_STATIC_CALL
#define LSM_HOOK_TRAMP(NAME, NUM) \
 &STATIC_CALL_TRAMP(LSM_STATIC_CALL(NAME, NUM))
#else
#define LSM_HOOK_TRAMP(NAME, NUM) NULL
#endif

/*
 * Define static calls and static keys for each LSM hook.
 */

#define DEFINE_LSM_STATIC_CALL(NUM, NAME, RET, ...)   \
 DEFINE_STATIC_CALL_NULL(LSM_STATIC_CALL(NAME, NUM),  \
    *((RET(*)(__VA_ARGS__))NULL));  \
 DEFINE_STATIC_KEY_FALSE(SECURITY_HOOK_ACTIVE_KEY(NAME, NUM));

#define LSM_HOOK(RET, DEFAULT, NAME, ...)    \
 LSM_DEFINE_UNROLL(DEFINE_LSM_STATIC_CALL, NAME, RET, __VA_ARGS__)
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
#undef DEFINE_LSM_STATIC_CALL

/*
 * Initialise a table of static calls for each LSM hook.
 * DEFINE_STATIC_CALL_NULL invocation above generates a key (STATIC_CALL_KEY)
 * and a trampoline (STATIC_CALL_TRAMP) which are used to call
 * __static_call_update when updating the static call.
 *
 * The static calls table is used by early LSMs, some architectures can fault on
 * unaligned accesses and the fault handling code may not be ready by then.
 * Thus, the static calls table should be aligned to avoid any unhandled faults
 * in early init.
 */

struct lsm_static_calls_table
 static_calls_table __ro_after_init __aligned(sizeof(u64)) = {
#define INIT_LSM_STATIC_CALL(NUM, NAME)     \
 (struct lsm_static_call) {     \
  .key = &STATIC_CALL_KEY(LSM_STATIC_CALL(NAME, NUM)), \
  .trampoline = LSM_HOOK_TRAMP(NAME, NUM),  \
  .active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM),  \
 },
#define LSM_HOOK(RET, DEFAULT, NAME, ...)    \
 .NAME = {       \
  LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME)  \
 },
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
#undef INIT_LSM_STATIC_CALL
 };

static __initdata bool debug;
#define init_debug(...)      \
 do {       \
  if (debug)     \
   pr_info(__VA_ARGS__);   \
 } while (0)

static bool __init is_enabled(struct lsm_info *lsm)
{
 if (!lsm->enabled)
  return false;

 return *lsm->enabled;
}

/* Mark an LSM's enabled flag. */
static int lsm_enabled_true __initdata = 1;
static int lsm_enabled_false __initdata = 0;
static void __init set_enabled(struct lsm_info *lsm, bool enabled)
{
 /*
 * When an LSM hasn't configured an enable variable, we can use
 * a hard-coded location for storing the default enabled state.
 */

 if (!lsm->enabled) {
  if (enabled)
   lsm->enabled = &lsm_enabled_true;
  else
   lsm->enabled = &lsm_enabled_false;
 } else if (lsm->enabled == &lsm_enabled_true) {
  if (!enabled)
   lsm->enabled = &lsm_enabled_false;
 } else if (lsm->enabled == &lsm_enabled_false) {
  if (enabled)
   lsm->enabled = &lsm_enabled_true;
 } else {
  *lsm->enabled = enabled;
 }
}

/* Is an LSM already listed in the ordered LSMs list? */
static bool __init exists_ordered_lsm(struct lsm_info *lsm)
{
 struct lsm_info **check;

 for (check = ordered_lsms; *check; check++)
  if (*check == lsm)
   return true;

 return false;
}

/* Append an LSM to the list of ordered LSMs to initialize. */
static int last_lsm __initdata;
static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
{
 /* Ignore duplicate selections. */
 if (exists_ordered_lsm(lsm))
  return;

 if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
  return;

 /* Enable this LSM, if it is not already set. */
 if (!lsm->enabled)
  lsm->enabled = &lsm_enabled_true;
 ordered_lsms[last_lsm++] = lsm;

 init_debug("%s ordered: %s (%s)\n", from, lsm->name,
     is_enabled(lsm) ? "enabled" : "disabled");
}

/* Is an LSM allowed to be initialized? */
static bool __init lsm_allowed(struct lsm_info *lsm)
{
 /* Skip if the LSM is disabled. */
 if (!is_enabled(lsm))
  return false;

 /* Not allowed if another exclusive LSM already initialized. */
 if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
  init_debug("exclusive disabled: %s\n", lsm->name);
  return false;
 }

 return true;
}

static void __init lsm_set_blob_size(int *need, int *lbs)
{
 int offset;

 if (*need <= 0)
  return;

 offset = ALIGN(*lbs, sizeof(void *));
 *lbs = offset + *need;
 *need = offset;
}

static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
{
 if (!needed)
  return;

 lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
 lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
 lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
 /*
 * The inode blob gets an rcu_head in addition to
 * what the modules might need.
 */

 if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
  blob_sizes.lbs_inode = sizeof(struct rcu_head);
 lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
 lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
 lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
 lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
 lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
 lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
 lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
 lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
 lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
 lsm_set_blob_size(&needed->lbs_xattr_count,
     &blob_sizes.lbs_xattr_count);
 lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
}

/* Prepare LSM for initialization. */
static void __init prepare_lsm(struct lsm_info *lsm)
{
 int enabled = lsm_allowed(lsm);

 /* Record enablement (to handle any following exclusive LSMs). */
 set_enabled(lsm, enabled);

 /* If enabled, do pre-initialization work. */
 if (enabled) {
  if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
   exclusive = lsm;
   init_debug("exclusive chosen: %s\n", lsm->name);
  }

  lsm_set_blob_sizes(lsm->blobs);
 }
}

/* Initialize a given LSM, if it is enabled. */
static void __init initialize_lsm(struct lsm_info *lsm)
{
 if (is_enabled(lsm)) {
  int ret;

  init_debug("initializing %s\n", lsm->name);
  ret = lsm->init();
  WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
 }
}

/*
 * Current index to use while initializing the lsm id list.
 */

u32 lsm_active_cnt __ro_after_init;
const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];

/* Populate ordered LSMs list from comma-separated LSM name list. */
static void __init ordered_lsm_parse(const char *order, const char *origin)
{
 struct lsm_info *lsm;
 char *sep, *name, *next;

 /* LSM_ORDER_FIRST is always first. */
 for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
  if (lsm->order == LSM_ORDER_FIRST)
   append_ordered_lsm(lsm, " first");
 }

 /* Process "security=", if given. */
 if (chosen_major_lsm) {
  struct lsm_info *major;

  /*
 * To match the original "security=" behavior, this
 * explicitly does NOT fallback to another Legacy Major
 * if the selected one was separately disabled: disable
 * all non-matching Legacy Major LSMs.
 */

  for (major = __start_lsm_info; major < __end_lsm_info;
       major++) {
   if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
       strcmp(major->name, chosen_major_lsm) != 0) {
    set_enabled(major, false);
    init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
        chosen_major_lsm, major->name);
   }
  }
 }

 sep = kstrdup(order, GFP_KERNEL);
 next = sep;
 /* Walk the list, looking for matching LSMs. */
 while ((name = strsep(&next, ",")) != NULL) {
  bool found = false;

  for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
   if (strcmp(lsm->name, name) == 0) {
    if (lsm->order == LSM_ORDER_MUTABLE)
     append_ordered_lsm(lsm, origin);
    found = true;
   }
  }

  if (!found)
   init_debug("%s ignored: %s (not built into kernel)\n",
       origin, name);
 }

 /* Process "security=", if given. */
 if (chosen_major_lsm) {
  for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
   if (exists_ordered_lsm(lsm))
    continue;
   if (strcmp(lsm->name, chosen_major_lsm) == 0)
    append_ordered_lsm(lsm, "security=");
  }
 }

 /* LSM_ORDER_LAST is always last. */
 for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
  if (lsm->order == LSM_ORDER_LAST)
   append_ordered_lsm(lsm, " last");
 }

 /* Disable all LSMs not in the ordered list. */
 for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
  if (exists_ordered_lsm(lsm))
   continue;
  set_enabled(lsm, false);
  init_debug("%s skipped: %s (not in requested order)\n",
      origin, lsm->name);
 }

 kfree(sep);
}

static void __init lsm_static_call_init(struct security_hook_list *hl)
{
 struct lsm_static_call *scall = hl->scalls;
 int i;

 for (i = 0; i < MAX_LSM_COUNT; i++) {
  /* Update the first static call that is not used yet */
  if (!scall->hl) {
   __static_call_update(scall->key, scall->trampoline,
          hl->hook.lsm_func_addr);
   scall->hl = hl;
   static_branch_enable(scall->active);
   return;
  }
  scall++;
 }
 panic("%s - Ran out of static slots.\n", __func__);
}

static void __init lsm_early_cred(struct cred *cred);
static void __init lsm_early_task(struct task_struct *task);

static int lsm_append(const char *newchar **result);

static void __init report_lsm_order(void)
{
 struct lsm_info **lsm, *early;
 int first = 0;

 pr_info("initializing lsm=");

 /* Report each enabled LSM name, comma separated. */
 for (early = __start_early_lsm_info;
      early < __end_early_lsm_info; early++)
  if (is_enabled(early))
   pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
 for (lsm = ordered_lsms; *lsm; lsm++)
  if (is_enabled(*lsm))
   pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);

 pr_cont("\n");
}

static void __init ordered_lsm_init(void)
{
 struct lsm_info **lsm;

 if (chosen_lsm_order) {
  if (chosen_major_lsm) {
   pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
    chosen_major_lsm, chosen_lsm_order);
   chosen_major_lsm = NULL;
  }
  ordered_lsm_parse(chosen_lsm_order, "cmdline");
 } else
  ordered_lsm_parse(builtin_lsm_order, "builtin");

 for (lsm = ordered_lsms; *lsm; lsm++)
  prepare_lsm(*lsm);

 report_lsm_order();

 init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
 init_debug("file blob size = %d\n", blob_sizes.lbs_file);
 init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
 init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
 init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
#ifdef CONFIG_KEYS
 init_debug("key blob size = %d\n", blob_sizes.lbs_key);
#endif /* CONFIG_KEYS */
 init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
 init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
 init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
 init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
 init_debug("task blob size = %d\n", blob_sizes.lbs_task);
 init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
 init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
 init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);

 /*
 * Create any kmem_caches needed for blobs
 */

 if (blob_sizes.lbs_file)
  lsm_file_cache = kmem_cache_create("lsm_file_cache",
         blob_sizes.lbs_file, 0,
         SLAB_PANIC, NULL);
 if (blob_sizes.lbs_inode)
  lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
          blob_sizes.lbs_inode, 0,
          SLAB_PANIC, NULL);

 lsm_early_cred((struct cred *) current->cred);
 lsm_early_task(current);
 for (lsm = ordered_lsms; *lsm; lsm++)
  initialize_lsm(*lsm);
}

int __init early_security_init(void)
{
 struct lsm_info *lsm;

 for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
  if (!lsm->enabled)
   lsm->enabled = &lsm_enabled_true;
  prepare_lsm(lsm);
  initialize_lsm(lsm);
 }

 return 0;
}

/**
 * security_init - initializes the security framework
 *
 * This should be called early in the kernel initialization sequence.
 */

int __init security_init(void)
{
 struct lsm_info *lsm;

 init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
 init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
 init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");

 /*
 * Append the names of the early LSM modules now that kmalloc() is
 * available
 */

 for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
  init_debug(" early started: %s (%s)\n", lsm->name,
      is_enabled(lsm) ? "enabled" : "disabled");
  if (lsm->enabled)
   lsm_append(lsm->name, &lsm_names);
 }

 /* Load LSMs in specified order. */
 ordered_lsm_init();

 return 0;
}

/* Save user chosen LSM */
static int __init choose_major_lsm(char *str)
{
 chosen_major_lsm = str;
 return 1;
}
__setup("security=", choose_major_lsm);

/* Explicitly choose LSM initialization order. */
static int __init choose_lsm_order(char *str)
{
 chosen_lsm_order = str;
 return 1;
}
__setup("lsm=", choose_lsm_order);

/* Enable LSM order debugging. */
static int __init enable_debug(char *str)
{
 debug = true;
 return 1;
}
__setup("lsm.debug", enable_debug);

static bool match_last_lsm(const char *list, const char *lsm)
{
 const char *last;

 if (WARN_ON(!list || !lsm))
  return false;
 last = strrchr(list, ',');
 if (last)
  /* Pass the comma, strcmp() will check for '\0' */
  last++;
 else
  last = list;
 return !strcmp(last, lsm);
}

static int lsm_append(const char *newchar **result)
{
 char *cp;

 if (*result == NULL) {
  *result = kstrdup(new, GFP_KERNEL);
  if (*result == NULL)
   return -ENOMEM;
 } else {
  /* Check if it is the last registered name */
  if (match_last_lsm(*result, new))
   return 0;
  cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
  if (cp == NULL)
   return -ENOMEM;
  kfree(*result);
  *result = cp;
 }
 return 0;
}

/**
 * security_add_hooks - Add a modules hooks to the hook lists.
 * @hooks: the hooks to add
 * @count: the number of hooks to add
 * @lsmid: the identification information for the security module
 *
 * Each LSM has to register its hooks with the infrastructure.
 */

void __init security_add_hooks(struct security_hook_list *hooks, int count,
          const struct lsm_id *lsmid)
{
 int i;

 /*
 * A security module may call security_add_hooks() more
 * than once during initialization, and LSM initialization
 * is serialized. Landlock is one such case.
 * Look at the previous entry, if there is one, for duplication.
 */

 if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
  if (lsm_active_cnt >= MAX_LSM_COUNT)
   panic("%s Too many LSMs registered.\n", __func__);
  lsm_idlist[lsm_active_cnt++] = lsmid;
 }

 for (i = 0; i < count; i++) {
  hooks[i].lsmid = lsmid;
  lsm_static_call_init(&hooks[i]);
 }

 /*
 * Don't try to append during early_security_init(), we'll come back
 * and fix this up afterwards.
 */

 if (slab_is_available()) {
  if (lsm_append(lsmid->name, &lsm_names) < 0)
   panic("%s - Cannot get early memory.\n", __func__);
 }
}

int call_blocking_lsm_notifier(enum lsm_event event, void *data)
{
 return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
         event, data);
}
EXPORT_SYMBOL(call_blocking_lsm_notifier);

int register_blocking_lsm_notifier(struct notifier_block *nb)
{
 return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
      nb);
}
EXPORT_SYMBOL(register_blocking_lsm_notifier);

int unregister_blocking_lsm_notifier(struct notifier_block *nb)
{
 return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
        nb);
}
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);

/**
 * lsm_blob_alloc - allocate a composite blob
 * @dest: the destination for the blob
 * @size: the size of the blob
 * @gfp: allocation type
 *
 * Allocate a blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
{
 if (size == 0) {
  *dest = NULL;
  return 0;
 }

 *dest = kzalloc(size, gfp);
 if (*dest == NULL)
  return -ENOMEM;
 return 0;
}

/**
 * lsm_cred_alloc - allocate a composite cred blob
 * @cred: the cred that needs a blob
 * @gfp: allocation type
 *
 * Allocate the cred blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
{
 return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
}

/**
 * lsm_early_cred - during initialization allocate a composite cred blob
 * @cred: the cred that needs a blob
 *
 * Allocate the cred blob for all the modules
 */

static void __init lsm_early_cred(struct cred *cred)
{
 int rc = lsm_cred_alloc(cred, GFP_KERNEL);

 if (rc)
  panic("%s: Early cred alloc failed.\n", __func__);
}

/**
 * lsm_file_alloc - allocate a composite file blob
 * @file: the file that needs a blob
 *
 * Allocate the file blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_file_alloc(struct file *file)
{
 if (!lsm_file_cache) {
  file->f_security = NULL;
  return 0;
 }

 file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
 if (file->f_security == NULL)
  return -ENOMEM;
 return 0;
}

/**
 * lsm_inode_alloc - allocate a composite inode blob
 * @inode: the inode that needs a blob
 * @gfp: allocation flags
 *
 * Allocate the inode blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
{
 if (!lsm_inode_cache) {
  inode->i_security = NULL;
  return 0;
 }

 inode->i_security = kmem_cache_zalloc(lsm_inode_cache, gfp);
 if (inode->i_security == NULL)
  return -ENOMEM;
 return 0;
}

/**
 * lsm_task_alloc - allocate a composite task blob
 * @task: the task that needs a blob
 *
 * Allocate the task blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_task_alloc(struct task_struct *task)
{
 return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
}

/**
 * lsm_ipc_alloc - allocate a composite ipc blob
 * @kip: the ipc that needs a blob
 *
 * Allocate the ipc blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_ipc_alloc(struct kern_ipc_perm *kip)
{
 return lsm_blob_alloc(&kip->security, blob_sizes.lbs_ipc, GFP_KERNEL);
}

#ifdef CONFIG_KEYS
/**
 * lsm_key_alloc - allocate a composite key blob
 * @key: the key that needs a blob
 *
 * Allocate the key blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_key_alloc(struct key *key)
{
 return lsm_blob_alloc(&key->security, blob_sizes.lbs_key, GFP_KERNEL);
}
#endif /* CONFIG_KEYS */

/**
 * lsm_msg_msg_alloc - allocate a composite msg_msg blob
 * @mp: the msg_msg that needs a blob
 *
 * Allocate the ipc blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_msg_msg_alloc(struct msg_msg *mp)
{
 return lsm_blob_alloc(&mp->security, blob_sizes.lbs_msg_msg,
         GFP_KERNEL);
}

/**
 * lsm_bdev_alloc - allocate a composite block_device blob
 * @bdev: the block_device that needs a blob
 *
 * Allocate the block_device blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_bdev_alloc(struct block_device *bdev)
{
 if (blob_sizes.lbs_bdev == 0) {
  bdev->bd_security = NULL;
  return 0;
 }

 bdev->bd_security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
 if (!bdev->bd_security)
  return -ENOMEM;

 return 0;
}

/**
 * lsm_early_task - during initialization allocate a composite task blob
 * @task: the task that needs a blob
 *
 * Allocate the task blob for all the modules
 */

static void __init lsm_early_task(struct task_struct *task)
{
 int rc = lsm_task_alloc(task);

 if (rc)
  panic("%s: Early task alloc failed.\n", __func__);
}

/**
 * lsm_superblock_alloc - allocate a composite superblock blob
 * @sb: the superblock that needs a blob
 *
 * Allocate the superblock blob for all the modules
 *
 * Returns 0, or -ENOMEM if memory can't be allocated.
 */

static int lsm_superblock_alloc(struct super_block *sb)
{
 return lsm_blob_alloc(&sb->s_security, blob_sizes.lbs_superblock,
         GFP_KERNEL);
}

/**
 * lsm_fill_user_ctx - Fill a user space lsm_ctx structure
 * @uctx: a userspace LSM context to be filled
 * @uctx_len: available uctx size (input), used uctx size (output)
 * @val: the new LSM context value
 * @val_len: the size of the new LSM context value
 * @id: LSM id
 * @flags: LSM defined flags
 *
 * Fill all of the fields in a userspace lsm_ctx structure.  If @uctx is NULL
 * simply calculate the required size to output via @utc_len and return
 * success.
 *
 * Returns 0 on success, -E2BIG if userspace buffer is not large enough,
 * -EFAULT on a copyout error, -ENOMEM if memory can't be allocated.
 */

int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
        void *val, size_t val_len,
        u64 id, u64 flags)
{
 struct lsm_ctx *nctx = NULL;
 size_t nctx_len;
 int rc = 0;

 nctx_len = ALIGN(struct_size(nctx, ctx, val_len), sizeof(void *));
 if (nctx_len > *uctx_len) {
  rc = -E2BIG;
  goto out;
 }

 /* no buffer - return success/0 and set @uctx_len to the req size */
 if (!uctx)
  goto out;

 nctx = kzalloc(nctx_len, GFP_KERNEL);
 if (nctx == NULL) {
  rc = -ENOMEM;
  goto out;
 }
 nctx->id = id;
 nctx->flags = flags;
 nctx->len = nctx_len;
 nctx->ctx_len = val_len;
 memcpy(nctx->ctx, val, val_len);

 if (copy_to_user(uctx, nctx, nctx_len))
  rc = -EFAULT;

out:
 kfree(nctx);
 *uctx_len = nctx_len;
 return rc;
}

/*
 * The default value of the LSM hook is defined in linux/lsm_hook_defs.h and
 * can be accessed with:
 *
 * LSM_RET_DEFAULT(<hook_name>)
 *
 * The macros below define static constants for the default value of each
 * LSM hook.
 */

#define LSM_RET_DEFAULT(NAME) (NAME##_default)
#define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME)
#define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \
 static const int __maybe_unused LSM_RET_DEFAULT(NAME) = (DEFAULT);
#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
 DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME)

#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK

/*
 * Hook list operation macros.
 *
 * call_void_hook:
 * This is a hook that does not return a value.
 *
 * call_int_hook:
 * This is a hook that returns a value.
 */

#define __CALL_STATIC_VOID(NUM, HOOK, ...)         \
do {              \
 if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {    \
  static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);      \
 }             \
while (0);

#define call_void_hook(HOOK, ...)                                 \
 do {                                                      \
  LSM_LOOP_UNROLL(__CALL_STATIC_VOID, HOOK, __VA_ARGS__); \
 } while (0)


#define __CALL_STATIC_INT(NUM, R, HOOK, LABEL, ...)        \
do {              \
 if (static_branch_unlikely(&SECURITY_HOOK_ACTIVE_KEY(HOOK, NUM))) {  \
  R = static_call(LSM_STATIC_CALL(HOOK, NUM))(__VA_ARGS__);    \
  if (R != LSM_RET_DEFAULT(HOOK))         \
   goto LABEL;          \
 }             \
while (0);

#define call_int_hook(HOOK, ...)     \
({         \
 __label__ OUT;       \
 int RC = LSM_RET_DEFAULT(HOOK);     \
         \
 LSM_LOOP_UNROLL(__CALL_STATIC_INT, RC, HOOK, OUT, __VA_ARGS__); \
OUT:         \
 RC;        \
})

#define lsm_for_each_hook(scall, NAME)     \
 for (scall = static_calls_table.NAME;    \
      scall - static_calls_table.NAME < MAX_LSM_COUNT; scall++)  \
  if (static_key_enabled(&scall->active->key))

/* Security operations */

/**
 * security_binder_set_context_mgr() - Check if becoming binder ctx mgr is ok
 * @mgr: task credentials of current binder process
 *
 * Check whether @mgr is allowed to be the binder context manager.
 *
 * Return: Return 0 if permission is granted.
 */

int security_binder_set_context_mgr(const struct cred *mgr)
{
 return call_int_hook(binder_set_context_mgr, mgr);
}

/**
 * security_binder_transaction() - Check if a binder transaction is allowed
 * @from: sending process
 * @to: receiving process
 *
 * Check whether @from is allowed to invoke a binder transaction call to @to.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_binder_transaction(const struct cred *from,
    const struct cred *to)
{
 return call_int_hook(binder_transaction, from, to);
}

/**
 * security_binder_transfer_binder() - Check if a binder transfer is allowed
 * @from: sending process
 * @to: receiving process
 *
 * Check whether @from is allowed to transfer a binder reference to @to.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_binder_transfer_binder(const struct cred *from,
        const struct cred *to)
{
 return call_int_hook(binder_transfer_binder, from, to);
}

/**
 * security_binder_transfer_file() - Check if a binder file xfer is allowed
 * @from: sending process
 * @to: receiving process
 * @file: file being transferred
 *
 * Check whether @from is allowed to transfer @file to @to.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_binder_transfer_file(const struct cred *from,
      const struct cred *to, const struct file *file)
{
 return call_int_hook(binder_transfer_file, from, to, file);
}

/**
 * security_ptrace_access_check() - Check if tracing is allowed
 * @child: target process
 * @mode: PTRACE_MODE flags
 *
 * Check permission before allowing the current process to trace the @child
 * process.  Security modules may also want to perform a process tracing check
 * during an execve in the set_security or apply_creds hooks of tracing check
 * during an execve in the bprm_set_creds hook of binprm_security_ops if the
 * process is being traced and its security attributes would be changed by the
 * execve.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
{
 return call_int_hook(ptrace_access_check, child, mode);
}

/**
 * security_ptrace_traceme() - Check if tracing is allowed
 * @parent: tracing process
 *
 * Check that the @parent process has sufficient permission to trace the
 * current process before allowing the current process to present itself to the
 * @parent process for tracing.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_ptrace_traceme(struct task_struct *parent)
{
 return call_int_hook(ptrace_traceme, parent);
}

/**
 * security_capget() - Get the capability sets for a process
 * @target: target process
 * @effective: effective capability set
 * @inheritable: inheritable capability set
 * @permitted: permitted capability set
 *
 * Get the @effective, @inheritable, and @permitted capability sets for the
 * @target process.  The hook may also perform permission checking to determine
 * if the current process is allowed to see the capability sets of the @target
 * process.
 *
 * Return: Returns 0 if the capability sets were successfully obtained.
 */

int security_capget(const struct task_struct *target,
      kernel_cap_t *effective,
      kernel_cap_t *inheritable,
      kernel_cap_t *permitted)
{
 return call_int_hook(capget, target, effective, inheritable, permitted);
}

/**
 * security_capset() - Set the capability sets for a process
 * @new: new credentials for the target process
 * @old: current credentials of the target process
 * @effective: effective capability set
 * @inheritable: inheritable capability set
 * @permitted: permitted capability set
 *
 * Set the @effective, @inheritable, and @permitted capability sets for the
 * current process.
 *
 * Return: Returns 0 and update @new if permission is granted.
 */

int security_capset(struct cred *newconst struct cred *old,
      const kernel_cap_t *effective,
      const kernel_cap_t *inheritable,
      const kernel_cap_t *permitted)
{
 return call_int_hook(capset, new, old, effective, inheritable,
        permitted);
}

/**
 * security_capable() - Check if a process has the necessary capability
 * @cred: credentials to examine
 * @ns: user namespace
 * @cap: capability requested
 * @opts: capability check options
 *
 * Check whether the @tsk process has the @cap capability in the indicated
 * credentials.  @cap contains the capability <include/linux/capability.h>.
 * @opts contains options for the capable check <include/linux/security.h>.
 *
 * Return: Returns 0 if the capability is granted.
 */

int security_capable(const struct cred *cred,
       struct user_namespace *ns,
       int cap,
       unsigned int opts)
{
 return call_int_hook(capable, cred, ns, cap, opts);
}

/**
 * security_quotactl() - Check if a quotactl() syscall is allowed for this fs
 * @cmds: commands
 * @type: type
 * @id: id
 * @sb: filesystem
 *
 * Check whether the quotactl syscall is allowed for this @sb.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_quotactl(int cmds, int type, int id, const struct super_block *sb)
{
 return call_int_hook(quotactl, cmds, type, id, sb);
}

/**
 * security_quota_on() - Check if QUOTAON is allowed for a dentry
 * @dentry: dentry
 *
 * Check whether QUOTAON is allowed for @dentry.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_quota_on(struct dentry *dentry)
{
 return call_int_hook(quota_on, dentry);
}

/**
 * security_syslog() - Check if accessing the kernel message ring is allowed
 * @type: SYSLOG_ACTION_* type
 *
 * Check permission before accessing the kernel message ring or changing
 * logging to the console.  See the syslog(2) manual page for an explanation of
 * the @type values.
 *
 * Return: Return 0 if permission is granted.
 */

int security_syslog(int type)
{
 return call_int_hook(syslog, type);
}

/**
 * security_settime64() - Check if changing the system time is allowed
 * @ts: new time
 * @tz: timezone
 *
 * Check permission to change the system time, struct timespec64 is defined in
 * <include/linux/time64.h> and timezone is defined in <include/linux/time.h>.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_settime64(const struct timespec64 *ts, const struct timezone *tz)
{
 return call_int_hook(settime, ts, tz);
}

/**
 * security_vm_enough_memory_mm() - Check if allocating a new mem map is allowed
 * @mm: mm struct
 * @pages: number of pages
 *
 * Check permissions for allocating a new virtual mapping.  If all LSMs return
 * a positive value, __vm_enough_memory() will be called with cap_sys_admin
 * set. If at least one LSM returns 0 or negative, __vm_enough_memory() will be
 * called with cap_sys_admin cleared.
 *
 * Return: Returns 0 if permission is granted by the LSM infrastructure to the
 *         caller.
 */

int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
 struct lsm_static_call *scall;
 int cap_sys_admin = 1;
 int rc;

 /*
 * The module will respond with 0 if it thinks the __vm_enough_memory()
 * call should be made with the cap_sys_admin set. If all of the modules
 * agree that it should be set it will. If any module thinks it should
 * not be set it won't.
 */

 lsm_for_each_hook(scall, vm_enough_memory) {
  rc = scall->hl->hook.vm_enough_memory(mm, pages);
  if (rc < 0) {
   cap_sys_admin = 0;
   break;
  }
 }
 return __vm_enough_memory(mm, pages, cap_sys_admin);
}

/**
 * security_bprm_creds_for_exec() - Prepare the credentials for exec()
 * @bprm: binary program information
 *
 * If the setup in prepare_exec_creds did not setup @bprm->cred->security
 * properly for executing @bprm->file, update the LSM's portion of
 * @bprm->cred->security to be what commit_creds needs to install for the new
 * program.  This hook may also optionally check permissions (e.g. for
 * transitions between security domains).  The hook must set @bprm->secureexec
 * to 1 if AT_SECURE should be set to request libc enable secure mode.  @bprm
 * contains the linux_binprm structure.
 *
 * If execveat(2) is called with the AT_EXECVE_CHECK flag, bprm->is_check is
 * set.  The result must be the same as without this flag even if the execution
 * will never really happen and @bprm will always be dropped.
 *
 * This hook must not change current->cred, only @bprm->cred.
 *
 * Return: Returns 0 if the hook is successful and permission is granted.
 */

int security_bprm_creds_for_exec(struct linux_binprm *bprm)
{
 return call_int_hook(bprm_creds_for_exec, bprm);
}

/**
 * security_bprm_creds_from_file() - Update linux_binprm creds based on file
 * @bprm: binary program information
 * @file: associated file
 *
 * If @file is setpcap, suid, sgid or otherwise marked to change privilege upon
 * exec, update @bprm->cred to reflect that change. This is called after
 * finding the binary that will be executed without an interpreter.  This
 * ensures that the credentials will not be derived from a script that the
 * binary will need to reopen, which when reopend may end up being a completely
 * different file.  This hook may also optionally check permissions (e.g. for
 * transitions between security domains).  The hook must set @bprm->secureexec
 * to 1 if AT_SECURE should be set to request libc enable secure mode.  The
 * hook must add to @bprm->per_clear any personality flags that should be
 * cleared from current->personality.  @bprm contains the linux_binprm
 * structure.
 *
 * Return: Returns 0 if the hook is successful and permission is granted.
 */

int security_bprm_creds_from_file(struct linux_binprm *bprm, const struct file *file)
{
 return call_int_hook(bprm_creds_from_file, bprm, file);
}

/**
 * security_bprm_check() - Mediate binary handler search
 * @bprm: binary program information
 *
 * This hook mediates the point when a search for a binary handler will begin.
 * It allows a check against the @bprm->cred->security value which was set in
 * the preceding creds_for_exec call.  The argv list and envp list are reliably
 * available in @bprm.  This hook may be called multiple times during a single
 * execve.  @bprm contains the linux_binprm structure.
 *
 * Return: Returns 0 if the hook is successful and permission is granted.
 */

int security_bprm_check(struct linux_binprm *bprm)
{
 return call_int_hook(bprm_check_security, bprm);
}

/**
 * security_bprm_committing_creds() - Install creds for a process during exec()
 * @bprm: binary program information
 *
 * Prepare to install the new security attributes of a process being
 * transformed by an execve operation, based on the old credentials pointed to
 * by @current->cred and the information set in @bprm->cred by the
 * bprm_creds_for_exec hook.  @bprm points to the linux_binprm structure.  This
 * hook is a good place to perform state changes on the process such as closing
 * open file descriptors to which access will no longer be granted when the
 * attributes are changed.  This is called immediately before commit_creds().
 */

void security_bprm_committing_creds(const struct linux_binprm *bprm)
{
 call_void_hook(bprm_committing_creds, bprm);
}

/**
 * security_bprm_committed_creds() - Tidy up after cred install during exec()
 * @bprm: binary program information
 *
 * Tidy up after the installation of the new security attributes of a process
 * being transformed by an execve operation.  The new credentials have, by this
 * point, been set to @current->cred.  @bprm points to the linux_binprm
 * structure.  This hook is a good place to perform state changes on the
 * process such as clearing out non-inheritable signal state.  This is called
 * immediately after commit_creds().
 */

void security_bprm_committed_creds(const struct linux_binprm *bprm)
{
 call_void_hook(bprm_committed_creds, bprm);
}

/**
 * security_fs_context_submount() - Initialise fc->security
 * @fc: new filesystem context
 * @reference: dentry reference for submount/remount
 *
 * Fill out the ->security field for a new fs_context.
 *
 * Return: Returns 0 on success or negative error code on failure.
 */

int security_fs_context_submount(struct fs_context *fc, struct super_block *reference)
{
 return call_int_hook(fs_context_submount, fc, reference);
}

/**
 * security_fs_context_dup() - Duplicate a fs_context LSM blob
 * @fc: destination filesystem context
 * @src_fc: source filesystem context
 *
 * Allocate and attach a security structure to sc->security.  This pointer is
 * initialised to NULL by the caller.  @fc indicates the new filesystem context.
 * @src_fc indicates the original filesystem context.
 *
 * Return: Returns 0 on success or a negative error code on failure.
 */

int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
{
 return call_int_hook(fs_context_dup, fc, src_fc);
}

/**
 * security_fs_context_parse_param() - Configure a filesystem context
 * @fc: filesystem context
 * @param: filesystem parameter
 *
 * Userspace provided a parameter to configure a superblock.  The LSM can
 * consume the parameter or return it to the caller for use elsewhere.
 *
 * Return: If the parameter is used by the LSM it should return 0, if it is
 *         returned to the caller -ENOPARAM is returned, otherwise a negative
 *         error code is returned.
 */

int security_fs_context_parse_param(struct fs_context *fc,
        struct fs_parameter *param)
{
 struct lsm_static_call *scall;
 int trc;
 int rc = -ENOPARAM;

 lsm_for_each_hook(scall, fs_context_parse_param) {
  trc = scall->hl->hook.fs_context_parse_param(fc, param);
  if (trc == 0)
   rc = 0;
  else if (trc != -ENOPARAM)
   return trc;
 }
 return rc;
}

/**
 * security_sb_alloc() - Allocate a super_block LSM blob
 * @sb: filesystem superblock
 *
 * Allocate and attach a security structure to the sb->s_security field.  The
 * s_security field is initialized to NULL when the structure is allocated.
 * @sb contains the super_block structure to be modified.
 *
 * Return: Returns 0 if operation was successful.
 */

int security_sb_alloc(struct super_block *sb)
{
 int rc = lsm_superblock_alloc(sb);

 if (unlikely(rc))
  return rc;
 rc = call_int_hook(sb_alloc_security, sb);
 if (unlikely(rc))
  security_sb_free(sb);
 return rc;
}

/**
 * security_sb_delete() - Release super_block LSM associated objects
 * @sb: filesystem superblock
 *
 * Release objects tied to a superblock (e.g. inodes).  @sb contains the
 * super_block structure being released.
 */

void security_sb_delete(struct super_block *sb)
{
 call_void_hook(sb_delete, sb);
}

/**
 * security_sb_free() - Free a super_block LSM blob
 * @sb: filesystem superblock
 *
 * Deallocate and clear the sb->s_security field.  @sb contains the super_block
 * structure to be modified.
 */

void security_sb_free(struct super_block *sb)
{
 call_void_hook(sb_free_security, sb);
 kfree(sb->s_security);
 sb->s_security = NULL;
}

/**
 * security_free_mnt_opts() - Free memory associated with mount options
 * @mnt_opts: LSM processed mount options
 *
 * Free memory associated with @mnt_ops.
 */

void security_free_mnt_opts(void **mnt_opts)
{
 if (!*mnt_opts)
  return;
 call_void_hook(sb_free_mnt_opts, *mnt_opts);
 *mnt_opts = NULL;
}
EXPORT_SYMBOL(security_free_mnt_opts);

/**
 * security_sb_eat_lsm_opts() - Consume LSM mount options
 * @options: mount options
 * @mnt_opts: LSM processed mount options
 *
 * Eat (scan @options) and save them in @mnt_opts.
 *
 * Return: Returns 0 on success, negative values on failure.
 */

int security_sb_eat_lsm_opts(char *options, void **mnt_opts)
{
 return call_int_hook(sb_eat_lsm_opts, options, mnt_opts);
}
EXPORT_SYMBOL(security_sb_eat_lsm_opts);

/**
 * security_sb_mnt_opts_compat() - Check if new mount options are allowed
 * @sb: filesystem superblock
 * @mnt_opts: new mount options
 *
 * Determine if the new mount options in @mnt_opts are allowed given the
 * existing mounted filesystem at @sb.  @sb superblock being compared.
 *
 * Return: Returns 0 if options are compatible.
 */

int security_sb_mnt_opts_compat(struct super_block *sb,
    void *mnt_opts)
{
 return call_int_hook(sb_mnt_opts_compat, sb, mnt_opts);
}
EXPORT_SYMBOL(security_sb_mnt_opts_compat);

/**
 * security_sb_remount() - Verify no incompatible mount changes during remount
 * @sb: filesystem superblock
 * @mnt_opts: (re)mount options
 *
 * Extracts security system specific mount options and verifies no changes are
 * being made to those options.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_sb_remount(struct super_block *sb,
   void *mnt_opts)
{
 return call_int_hook(sb_remount, sb, mnt_opts);
}
EXPORT_SYMBOL(security_sb_remount);

/**
 * security_sb_kern_mount() - Check if a kernel mount is allowed
 * @sb: filesystem superblock
 *
 * Mount this @sb if allowed by permissions.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_sb_kern_mount(const struct super_block *sb)
{
 return call_int_hook(sb_kern_mount, sb);
}

/**
 * security_sb_show_options() - Output the mount options for a superblock
 * @m: output file
 * @sb: filesystem superblock
 *
 * Show (print on @m) mount options for this @sb.
 *
 * Return: Returns 0 on success, negative values on failure.
 */

int security_sb_show_options(struct seq_file *m, struct super_block *sb)
{
 return call_int_hook(sb_show_options, m, sb);
}

/**
 * security_sb_statfs() - Check if accessing fs stats is allowed
 * @dentry: superblock handle
 *
 * Check permission before obtaining filesystem statistics for the @mnt
 * mountpoint.  @dentry is a handle on the superblock for the filesystem.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_sb_statfs(struct dentry *dentry)
{
 return call_int_hook(sb_statfs, dentry);
}

/**
 * security_sb_mount() - Check permission for mounting a filesystem
 * @dev_name: filesystem backing device
 * @path: mount point
 * @type: filesystem type
 * @flags: mount flags
 * @data: filesystem specific data
 *
 * Check permission before an object specified by @dev_name is mounted on the
 * mount point named by @nd.  For an ordinary mount, @dev_name identifies a
 * device if the file system type requires a device.  For a remount
 * (@flags & MS_REMOUNT), @dev_name is irrelevant.  For a loopback/bind mount
 * (@flags & MS_BIND), @dev_name identifies the pathname of the object being
 * mounted.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_sb_mount(const char *dev_name, const struct path *path,
        const char *type, unsigned long flags, void *data)
{
 return call_int_hook(sb_mount, dev_name, path, type, flags, data);
}

/**
 * security_sb_umount() - Check permission for unmounting a filesystem
 * @mnt: mounted filesystem
 * @flags: unmount flags
 *
 * Check permission before the @mnt file system is unmounted.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_sb_umount(struct vfsmount *mnt, int flags)
{
 return call_int_hook(sb_umount, mnt, flags);
}

/**
 * security_sb_pivotroot() - Check permissions for pivoting the rootfs
 * @old_path: new location for current rootfs
 * @new_path: location of the new rootfs
 *
 * Check permission before pivoting the root filesystem.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_sb_pivotroot(const struct path *old_path,
     const struct path *new_path)
{
 return call_int_hook(sb_pivotroot, old_path, new_path);
}

/**
 * security_sb_set_mnt_opts() - Set the mount options for a filesystem
 * @sb: filesystem superblock
 * @mnt_opts: binary mount options
 * @kern_flags: kernel flags (in)
 * @set_kern_flags: kernel flags (out)
 *
 * Set the security relevant mount options used for a superblock.
 *
 * Return: Returns 0 on success, error on failure.
 */

int security_sb_set_mnt_opts(struct super_block *sb,
        void *mnt_opts,
        unsigned long kern_flags,
        unsigned long *set_kern_flags)
{
 struct lsm_static_call *scall;
 int rc = mnt_opts ? -EOPNOTSUPP : LSM_RET_DEFAULT(sb_set_mnt_opts);

 lsm_for_each_hook(scall, sb_set_mnt_opts) {
  rc = scall->hl->hook.sb_set_mnt_opts(sb, mnt_opts, kern_flags,
           set_kern_flags);
  if (rc != LSM_RET_DEFAULT(sb_set_mnt_opts))
   break;
 }
 return rc;
}
EXPORT_SYMBOL(security_sb_set_mnt_opts);

/**
 * security_sb_clone_mnt_opts() - Duplicate superblock mount options
 * @oldsb: source superblock
 * @newsb: destination superblock
 * @kern_flags: kernel flags (in)
 * @set_kern_flags: kernel flags (out)
 *
 * Copy all security options from a given superblock to another.
 *
 * Return: Returns 0 on success, error on failure.
 */

int security_sb_clone_mnt_opts(const struct super_block *oldsb,
          struct super_block *newsb,
          unsigned long kern_flags,
          unsigned long *set_kern_flags)
{
 return call_int_hook(sb_clone_mnt_opts, oldsb, newsb,
        kern_flags, set_kern_flags);
}
EXPORT_SYMBOL(security_sb_clone_mnt_opts);

/**
 * security_move_mount() - Check permissions for moving a mount
 * @from_path: source mount point
 * @to_path: destination mount point
 *
 * Check permission before a mount is moved.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_move_mount(const struct path *from_path,
   const struct path *to_path)
{
 return call_int_hook(move_mount, from_path, to_path);
}

/**
 * security_path_notify() - Check if setting a watch is allowed
 * @path: file path
 * @mask: event mask
 * @obj_type: file path type
 *
 * Check permissions before setting a watch on events as defined by @mask, on
 * an object at @path, whose type is defined by @obj_type.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_notify(const struct path *path, u64 mask,
    unsigned int obj_type)
{
 return call_int_hook(path_notify, path, mask, obj_type);
}

/**
 * security_inode_alloc() - Allocate an inode LSM blob
 * @inode: the inode
 * @gfp: allocation flags
 *
 * Allocate and attach a security structure to @inode->i_security.  The
 * i_security field is initialized to NULL when the inode structure is
 * allocated.
 *
 * Return: Return 0 if operation was successful.
 */

int security_inode_alloc(struct inode *inode, gfp_t gfp)
{
 int rc = lsm_inode_alloc(inode, gfp);

 if (unlikely(rc))
  return rc;
 rc = call_int_hook(inode_alloc_security, inode);
 if (unlikely(rc))
  security_inode_free(inode);
 return rc;
}

static void inode_free_by_rcu(struct rcu_head *head)
{
 /* The rcu head is at the start of the inode blob */
 call_void_hook(inode_free_security_rcu, head);
 kmem_cache_free(lsm_inode_cache, head);
}

/**
 * security_inode_free() - Free an inode's LSM blob
 * @inode: the inode
 *
 * Release any LSM resources associated with @inode, although due to the
 * inode's RCU protections it is possible that the resources will not be
 * fully released until after the current RCU grace period has elapsed.
 *
 * It is important for LSMs to note that despite being present in a call to
 * security_inode_free(), @inode may still be referenced in a VFS path walk
 * and calls to security_inode_permission() may be made during, or after,
 * a call to security_inode_free().  For this reason the inode->i_security
 * field is released via a call_rcu() callback and any LSMs which need to
 * retain inode state for use in security_inode_permission() should only
 * release that state in the inode_free_security_rcu() LSM hook callback.
 */

void security_inode_free(struct inode *inode)
{
 call_void_hook(inode_free_security, inode);
 if (!inode->i_security)
  return;
 call_rcu((struct rcu_head *)inode->i_security, inode_free_by_rcu);
}

/**
 * security_dentry_init_security() - Perform dentry initialization
 * @dentry: the dentry to initialize
 * @mode: mode used to determine resource type
 * @name: name of the last path component
 * @xattr_name: name of the security/LSM xattr
 * @lsmctx: pointer to the resulting LSM context
 *
 * Compute a context for a dentry as the inode is not yet available since NFSv4
 * has no label backed by an EA anyway.  It is important to note that
 * @xattr_name does not need to be free'd by the caller, it is a static string.
 *
 * Return: Returns 0 on success, negative values on failure.
 */

int security_dentry_init_security(struct dentry *dentry, int mode,
      const struct qstr *name,
      const char **xattr_name,
      struct lsm_context *lsmctx)
{
 return call_int_hook(dentry_init_security, dentry, mode, name,
        xattr_name, lsmctx);
}
EXPORT_SYMBOL(security_dentry_init_security);

/**
 * security_dentry_create_files_as() - Perform dentry initialization
 * @dentry: the dentry to initialize
 * @mode: mode used to determine resource type
 * @name: name of the last path component
 * @old: creds to use for LSM context calculations
 * @new: creds to modify
 *
 * Compute a context for a dentry as the inode is not yet available and set
 * that context in passed in creds so that new files are created using that
 * context. Context is calculated using the passed in creds and not the creds
 * of the caller.
 *
 * Return: Returns 0 on success, error on failure.
 */

int security_dentry_create_files_as(struct dentry *dentry, int mode,
        struct qstr *name,
        const struct cred *old, struct cred *new)
{
 return call_int_hook(dentry_create_files_as, dentry, mode,
        name, old, new);
}
EXPORT_SYMBOL(security_dentry_create_files_as);

/**
 * security_inode_init_security() - Initialize an inode's LSM context
 * @inode: the inode
 * @dir: parent directory
 * @qstr: last component of the pathname
 * @initxattrs: callback function to write xattrs
 * @fs_data: filesystem specific data
 *
 * Obtain the security attribute name suffix and value to set on a newly
 * created inode and set up the incore security field for the new inode.  This
 * hook is called by the fs code as part of the inode creation transaction and
 * provides for atomic labeling of the inode, unlike the post_create/mkdir/...
 * hooks called by the VFS.
 *
 * The hook function is expected to populate the xattrs array, by calling
 * lsm_get_xattr_slot() to retrieve the slots reserved by the security module
 * with the lbs_xattr_count field of the lsm_blob_sizes structure.  For each
 * slot, the hook function should set ->name to the attribute name suffix
 * (e.g. selinux), to allocate ->value (will be freed by the caller) and set it
 * to the attribute value, to set ->value_len to the length of the value.  If
 * the security module does not use security attributes or does not wish to put
 * a security attribute on this particular inode, then it should return
 * -EOPNOTSUPP to skip this processing.
 *
 * Return: Returns 0 if the LSM successfully initialized all of the inode
 *         security attributes that are required, negative values otherwise.
 */

int security_inode_init_security(struct inode *inode, struct inode *dir,
     const struct qstr *qstr,
     const initxattrs initxattrs, void *fs_data)
{
 struct lsm_static_call *scall;
 struct xattr *new_xattrs = NULL;
 int ret = -EOPNOTSUPP, xattr_count = 0;

 if (unlikely(IS_PRIVATE(inode)))
  return 0;

 if (!blob_sizes.lbs_xattr_count)
  return 0;

 if (initxattrs) {
  /* Allocate +1 as terminator. */
  new_xattrs = kcalloc(blob_sizes.lbs_xattr_count + 1,
         sizeof(*new_xattrs), GFP_NOFS);
  if (!new_xattrs)
   return -ENOMEM;
 }

 lsm_for_each_hook(scall, inode_init_security) {
  ret = scall->hl->hook.inode_init_security(inode, dir, qstr, new_xattrs,
        &xattr_count);
  if (ret && ret != -EOPNOTSUPP)
   goto out;
  /*
 * As documented in lsm_hooks.h, -EOPNOTSUPP in this context
 * means that the LSM is not willing to provide an xattr, not
 * that it wants to signal an error. Thus, continue to invoke
 * the remaining LSMs.
 */

 }

 /* If initxattrs() is NULL, xattr_count is zero, skip the call. */
 if (!xattr_count)
  goto out;

 ret = initxattrs(inode, new_xattrs, fs_data);
out:
 for (; xattr_count > 0; xattr_count--)
  kfree(new_xattrs[xattr_count - 1].value);
 kfree(new_xattrs);
 return (ret == -EOPNOTSUPP) ? 0 : ret;
}
EXPORT_SYMBOL(security_inode_init_security);

/**
 * security_inode_init_security_anon() - Initialize an anonymous inode
 * @inode: the inode
 * @name: the anonymous inode class
 * @context_inode: an optional related inode
 *
 * Set up the incore security field for the new anonymous inode and return
 * whether the inode creation is permitted by the security module or not.
 *
 * Return: Returns 0 on success, -EACCES if the security module denies the
 * creation of this inode, or another -errno upon other errors.
 */

int security_inode_init_security_anon(struct inode *inode,
          const struct qstr *name,
          const struct inode *context_inode)
{
 return call_int_hook(inode_init_security_anon, inode, name,
        context_inode);
}

#ifdef CONFIG_SECURITY_PATH
/**
 * security_path_mknod() - Check if creating a special file is allowed
 * @dir: parent directory
 * @dentry: new file
 * @mode: new file mode
 * @dev: device number
 *
 * Check permissions when creating a file. Note that this hook is called even
 * if mknod operation is being done for a regular file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_mknod(const struct path *dir, struct dentry *dentry,
   umode_t mode, unsigned int dev)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
  return 0;
 return call_int_hook(path_mknod, dir, dentry, mode, dev);
}
EXPORT_SYMBOL(security_path_mknod);

/**
 * security_path_post_mknod() - Update inode security after reg file creation
 * @idmap: idmap of the mount
 * @dentry: new file
 *
 * Update inode security field after a regular file has been created.
 */

void security_path_post_mknod(struct mnt_idmap *idmap, struct dentry *dentry)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
  return;
 call_void_hook(path_post_mknod, idmap, dentry);
}

/**
 * security_path_mkdir() - Check if creating a new directory is allowed
 * @dir: parent directory
 * @dentry: new directory
 * @mode: new directory mode
 *
 * Check permissions to create a new directory in the existing directory.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_mkdir(const struct path *dir, struct dentry *dentry,
   umode_t mode)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
  return 0;
 return call_int_hook(path_mkdir, dir, dentry, mode);
}
EXPORT_SYMBOL(security_path_mkdir);

/**
 * security_path_rmdir() - Check if removing a directory is allowed
 * @dir: parent directory
 * @dentry: directory to remove
 *
 * Check the permission to remove a directory.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_rmdir(const struct path *dir, struct dentry *dentry)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
  return 0;
 return call_int_hook(path_rmdir, dir, dentry);
}

/**
 * security_path_unlink() - Check if removing a hard link is allowed
 * @dir: parent directory
 * @dentry: file
 *
 * Check the permission to remove a hard link to a file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_unlink(const struct path *dir, struct dentry *dentry)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
  return 0;
 return call_int_hook(path_unlink, dir, dentry);
}
EXPORT_SYMBOL(security_path_unlink);

/**
 * security_path_symlink() - Check if creating a symbolic link is allowed
 * @dir: parent directory
 * @dentry: symbolic link
 * @old_name: file pathname
 *
 * Check the permission to create a symbolic link to a file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_symlink(const struct path *dir, struct dentry *dentry,
     const char *old_name)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dir->dentry))))
  return 0;
 return call_int_hook(path_symlink, dir, dentry, old_name);
}

/**
 * security_path_link - Check if creating a hard link is allowed
 * @old_dentry: existing file
 * @new_dir: new parent directory
 * @new_dentry: new link
 *
 * Check permission before creating a new hard link to a file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
         struct dentry *new_dentry)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
  return 0;
 return call_int_hook(path_link, old_dentry, new_dir, new_dentry);
}

/**
 * security_path_rename() - Check if renaming a file is allowed
 * @old_dir: parent directory of the old file
 * @old_dentry: the old file
 * @new_dir: parent directory of the new file
 * @new_dentry: the new file
 * @flags: flags
 *
 * Check for permission to rename a file or directory.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
    const struct path *new_dir, struct dentry *new_dentry,
    unsigned int flags)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry)) ||
       (d_is_positive(new_dentry) &&
        IS_PRIVATE(d_backing_inode(new_dentry)))))
  return 0;

 return call_int_hook(path_rename, old_dir, old_dentry, new_dir,
        new_dentry, flags);
}
EXPORT_SYMBOL(security_path_rename);

/**
 * security_path_truncate() - Check if truncating a file is allowed
 * @path: file
 *
 * Check permission before truncating the file indicated by path.  Note that
 * truncation permissions may also be checked based on already opened files,
 * using the security_file_truncate() hook.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_truncate(const struct path *path)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
  return 0;
 return call_int_hook(path_truncate, path);
}

/**
 * security_path_chmod() - Check if changing the file's mode is allowed
 * @path: file
 * @mode: new mode
 *
 * Check for permission to change a mode of the file @path. The new mode is
 * specified in @mode which is a bitmask of constants from
 * <include/uapi/linux/stat.h>.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_chmod(const struct path *path, umode_t mode)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
  return 0;
 return call_int_hook(path_chmod, path, mode);
}

/**
 * security_path_chown() - Check if changing the file's owner/group is allowed
 * @path: file
 * @uid: file owner
 * @gid: file group
 *
 * Check for permission to change owner/group of a file or directory.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(path->dentry))))
  return 0;
 return call_int_hook(path_chown, path, uid, gid);
}

/**
 * security_path_chroot() - Check if changing the root directory is allowed
 * @path: directory
 *
 * Check for permission to change root directory.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_path_chroot(const struct path *path)
{
 return call_int_hook(path_chroot, path);
}
#endif /* CONFIG_SECURITY_PATH */

/**
 * security_inode_create() - Check if creating a file is allowed
 * @dir: the parent directory
 * @dentry: the file being created
 * @mode: requested file mode
 *
 * Check permission to create a regular file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_inode_create(struct inode *dir, struct dentry *dentry,
     umode_t mode)
{
 if (unlikely(IS_PRIVATE(dir)))
  return 0;
 return call_int_hook(inode_create, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_create);

/**
 * security_inode_post_create_tmpfile() - Update inode security of new tmpfile
 * @idmap: idmap of the mount
 * @inode: inode of the new tmpfile
 *
 * Update inode security data after a tmpfile has been created.
 */

void security_inode_post_create_tmpfile(struct mnt_idmap *idmap,
     struct inode *inode)
{
 if (unlikely(IS_PRIVATE(inode)))
  return;
 call_void_hook(inode_post_create_tmpfile, idmap, inode);
}

/**
 * security_inode_link() - Check if creating a hard link is allowed
 * @old_dentry: existing file
 * @dir: new parent directory
 * @new_dentry: new link
 *
 * Check permission before creating a new hard link to a file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_inode_link(struct dentry *old_dentry, struct inode *dir,
   struct dentry *new_dentry)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(old_dentry))))
  return 0;
 return call_int_hook(inode_link, old_dentry, dir, new_dentry);
}

/**
 * security_inode_unlink() - Check if removing a hard link is allowed
 * @dir: parent directory
 * @dentry: file
 *
 * Check the permission to remove a hard link to a file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_inode_unlink(struct inode *dir, struct dentry *dentry)
{
 if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
  return 0;
 return call_int_hook(inode_unlink, dir, dentry);
}

/**
 * security_inode_symlink() - Check if creating a symbolic link is allowed
 * @dir: parent directory
 * @dentry: symbolic link
 * @old_name: existing filename
 *
 * Check the permission to create a symbolic link to a file.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_inode_symlink(struct inode *dir, struct dentry *dentry,
      const char *old_name)
{
 if (unlikely(IS_PRIVATE(dir)))
  return 0;
 return call_int_hook(inode_symlink, dir, dentry, old_name);
}

/**
 * security_inode_mkdir() - Check if creating a new directory is allowed
 * @dir: parent directory
 * @dentry: new directory
 * @mode: new directory mode
 *
 * Check permissions to create a new directory in the existing directory
 * associated with inode structure @dir.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_inode_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
 if (unlikely(IS_PRIVATE(dir)))
  return 0;
 return call_int_hook(inode_mkdir, dir, dentry, mode);
}
EXPORT_SYMBOL_GPL(security_inode_mkdir);

/**
 * security_inode_rmdir() - Check if removing a directory is allowed
 * @dir: parent directory
 * @dentry: directory to be removed
 *
 * Check the permission to remove a directory.
 *
 * Return: Returns 0 if permission is granted.
 */

int security_inode_rmdir(struct inode *dir, struct dentry *dentry)
{
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=92 G=93

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge