Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. */#include <linux/fs.h>#include <linux/namei.h>#include <linux/types.h>#include <linux/dcache.h>#include <linux/security.h>
#include "ipe.h"#include "policy.h"#include "eval.h"#include "fs.h"#include "audit.h"
#define MAX_VERSION_SIZE ARRAY_SIZE("65535.65535.65535")
/** * struct ipefs_file - defines a file in securityfs. * * @name: file name inside the policy subdirectory * @access: file permissions * @fops: &file_operations specific to this file */struct ipefs_file { const char *name; umode_t access; const struct file_operations *fops;};
/** * read_pkcs7() - Read handler for "ipe/policies/$name/pkcs7". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * @data will be populated with the pkcs7 blob representing the policy * on success. If the policy is unsigned (like the boot policy), this * will return -ENOENT. * * Return: * * Length of buffer written - Success * * %-ENOENT - Policy initializing/deleted or is unsigned */static ssize_t read_pkcs7(struct file *f, char __user *data, size_t len, loff_t *offset){ const struct ipe_policy *p = NULL; struct inode *root = NULL; int rc = 0;
root = d_inode(f->f_path.dentry->d_parent);
inode_lock_shared(root); p = (struct ipe_policy *)root->i_private; if (!p) { rc = -ENOENT; goto out; }
if (!p->pkcs7) { rc = -ENOENT; goto out; }
rc = simple_read_from_buffer(data, len, offset, p->pkcs7, p->pkcs7len);
out: inode_unlock_shared(root);
return rc;}
/** * read_policy() - Read handler for "ipe/policies/$name/policy". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * @data will be populated with the plain-text version of the policy * on success. * * Return: * * Length of buffer written - Success * * %-ENOENT - Policy initializing/deleted */static ssize_t read_policy(struct file *f, char __user *data, size_t len, loff_t *offset){ const struct ipe_policy *p = NULL; struct inode *root = NULL; int rc = 0;
root = d_inode(f->f_path.dentry->d_parent);
inode_lock_shared(root); p = (struct ipe_policy *)root->i_private; if (!p) { rc = -ENOENT; goto out; }
rc = simple_read_from_buffer(data, len, offset, p->text, p->textlen);
out: inode_unlock_shared(root);
return rc;}
/** * read_name() - Read handler for "ipe/policies/$name/name". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * @data will be populated with the policy_name attribute on success. * * Return: * * Length of buffer written - Success * * %-ENOENT - Policy initializing/deleted */static ssize_t read_name(struct file *f, char __user *data, size_t len, loff_t *offset){ const struct ipe_policy *p = NULL; struct inode *root = NULL; int rc = 0;
root = d_inode(f->f_path.dentry->d_parent);
inode_lock_shared(root); p = (struct ipe_policy *)root->i_private; if (!p) { rc = -ENOENT; goto out; }
rc = simple_read_from_buffer(data, len, offset, p->parsed->name, strlen(p->parsed->name));
out: inode_unlock_shared(root);
return rc;}
/** * read_version() - Read handler for "ipe/policies/$name/version". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * @data will be populated with the version string on success. * * Return: * * Length of buffer written - Success * * %-ENOENT - Policy initializing/deleted */static ssize_t read_version(struct file *f, char __user *data, size_t len, loff_t *offset){ char buffer[MAX_VERSION_SIZE] = { 0 }; const struct ipe_policy *p = NULL; struct inode *root = NULL; size_t strsize = 0; ssize_t rc = 0;
root = d_inode(f->f_path.dentry->d_parent);
inode_lock_shared(root); p = (struct ipe_policy *)root->i_private; if (!p) { rc = -ENOENT; goto out; }
strsize = scnprintf(buffer, ARRAY_SIZE(buffer), "%hu.%hu.%hu", p->parsed->version.major, p->parsed->version.minor, p->parsed->version.rev);
rc = simple_read_from_buffer(data, len, offset, buffer, strsize);
out: inode_unlock_shared(root);
return rc;}
/** * setactive() - Write handler for "ipe/policies/$name/active". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * Return: * * Length of buffer written - Success * * %-EPERM - Insufficient permission * * %-EINVAL - Invalid input * * %-ENOENT - Policy initializing/deleted */static ssize_t setactive(struct file *f, const char __user *data, size_t len, loff_t *offset){ const struct ipe_policy *p = NULL; struct inode *root = NULL; bool value = false; int rc = 0;
if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) return -EPERM;
rc = kstrtobool_from_user(data, len, &value); if (rc) return rc;
if (!value) return -EINVAL;
root = d_inode(f->f_path.dentry->d_parent); inode_lock(root);
p = (struct ipe_policy *)root->i_private; if (!p) { rc = -ENOENT; goto out; }
rc = ipe_set_active_pol(p);
out: inode_unlock(root); return (rc < 0) ? rc : len;}
/** * getactive() - Read handler for "ipe/policies/$name/active". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * @data will be populated with the 1 or 0 depending on if the * corresponding policy is active. * * Return: * * Length of buffer written - Success * * %-ENOENT - Policy initializing/deleted */static ssize_t getactive(struct file *f, char __user *data, size_t len, loff_t *offset){ const struct ipe_policy *p = NULL; struct inode *root = NULL; const char *str; int rc = 0;
root = d_inode(f->f_path.dentry->d_parent);
inode_lock_shared(root); p = (struct ipe_policy *)root->i_private; if (!p) { inode_unlock_shared(root); return -ENOENT; } inode_unlock_shared(root);
str = (p == rcu_access_pointer(ipe_active_policy)) ? "1" : "0"; rc = simple_read_from_buffer(data, len, offset, str, 1);
return rc;}
/** * update_policy() - Write handler for "ipe/policies/$name/update". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * On success this updates the policy represented by $name, * in-place. * * Return: * * Length of buffer written - Success * * %-EPERM - Insufficient permission * * %-ENOMEM - Out of memory (OOM) * * %-ENOENT - Policy was deleted while updating * * %-EINVAL - Policy name mismatch * * %-ESTALE - Policy version too old */static ssize_t update_policy(struct file *f, const char __user *data, size_t len, loff_t *offset){ struct inode *root = NULL; char *copy = NULL; int rc = 0;
if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) { rc = -EPERM; goto out; }
copy = memdup_user(data, len); if (IS_ERR(copy)) { rc = PTR_ERR(copy); copy = NULL; goto out; }
root = d_inode(f->f_path.dentry->d_parent); inode_lock(root); rc = ipe_update_policy(root, NULL, 0, copy, len); inode_unlock(root);
out: kfree(copy); if (rc) { ipe_audit_policy_load(ERR_PTR(rc)); return rc; }
return len;}
/** * delete_policy() - write handler for "ipe/policies/$name/delete". * @f: Supplies a file structure representing the securityfs node. * @data: Supplies a buffer passed to the write syscall. * @len: Supplies the length of @data. * @offset: unused. * * On success this deletes the policy represented by $name. * * Return: * * Length of buffer written - Success * * %-EPERM - Insufficient permission/deleting active policy * * %-EINVAL - Invalid input * * %-ENOENT - Policy initializing/deleted */static ssize_t delete_policy(struct file *f, const char __user *data, size_t len, loff_t *offset){ struct ipe_policy *ap = NULL; struct ipe_policy *p = NULL; struct inode *root = NULL; bool value = false; int rc = 0;
if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN)) return -EPERM;
rc = kstrtobool_from_user(data, len, &value); if (rc) return rc;
if (!value) return -EINVAL;
root = d_inode(f->f_path.dentry->d_parent); inode_lock(root); p = (struct ipe_policy *)root->i_private; if (!p) { inode_unlock(root); return -ENOENT; }
mutex_lock(&ipe_policy_lock); ap = rcu_dereference_protected(ipe_active_policy, lockdep_is_held(&ipe_policy_lock)); if (p == ap) { mutex_unlock(&ipe_policy_lock); inode_unlock(root); return -EPERM; } mutex_unlock(&ipe_policy_lock);
root->i_private = NULL; inode_unlock(root);
synchronize_rcu(); ipe_free_policy(p);
return len;}
static const struct file_operations content_fops = { .read = read_policy,};
static const struct file_operations pkcs7_fops = { .read = read_pkcs7,};
static const struct file_operations name_fops = { .read = read_name,};
static const struct file_operations ver_fops = { .read = read_version,};
static const struct file_operations active_fops = { .write = setactive, .read = getactive,};
static const struct file_operations update_fops = { .write = update_policy,};
static const struct file_operations delete_fops = { .write = delete_policy,};
/* * policy_subdir - files under a policy subdirectory */static const struct ipefs_file policy_subdir[] = { { "pkcs7", 0444, &pkcs7_fops }, { "policy", 0444, &content_fops }, { "name", 0444, &name_fops }, { "version", 0444, &ver_fops }, { "active", 0600, &active_fops }, { "update", 0200, &update_fops }, { "delete", 0200, &delete_fops },};
/** * ipe_del_policyfs_node() - Delete a securityfs entry for @p. * @p: Supplies a pointer to the policy to delete a securityfs entry for. */void ipe_del_policyfs_node(struct ipe_policy *p){ securityfs_remove(p->policyfs); p->policyfs = NULL;}
/** * ipe_new_policyfs_node() - Create a securityfs entry for @p. * @p: Supplies a pointer to the policy to create a securityfs entry for. * * Return: %0 on success. If an error occurs, the function will return * the -errno. */int ipe_new_policyfs_node(struct ipe_policy *p){ const struct ipefs_file *f = NULL; struct dentry *policyfs = NULL; struct inode *root = NULL; struct dentry *d = NULL; size_t i = 0; int rc = 0;
if (p->policyfs) return 0;
policyfs = securityfs_create_dir(p->parsed->name, policy_root); if (IS_ERR(policyfs)) return PTR_ERR(policyfs);
root = d_inode(policyfs);
for (i = 0; i < ARRAY_SIZE(policy_subdir); ++i) { f = &policy_subdir[i];
d = securityfs_create_file(f->name, f->access, policyfs, NULL, f->fops); if (IS_ERR(d)) { rc = PTR_ERR(d); goto err; } }
inode_lock(root); p->policyfs = policyfs; root->i_private = p; /* Only audit signed policies from userspace */ if (p->pkcs7) ipe_audit_policy_load(p); inode_unlock(root);
return 0;err: securityfs_remove(policyfs); return rc;}