diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /security/apparmor/include | |
parent | Initial commit. (diff) | |
download | linux-upstream.tar.xz linux-upstream.zip |
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
24 files changed, 3210 insertions, 0 deletions
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h new file mode 100644 index 000000000..9c3fc36a0 --- /dev/null +++ b/security/apparmor/include/apparmor.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor basic global + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + */ + +#ifndef __APPARMOR_H +#define __APPARMOR_H + +#include <linux/types.h> + +/* + * Class of mediation types in the AppArmor policy db + */ +#define AA_CLASS_ENTRY 0 +#define AA_CLASS_UNKNOWN 1 +#define AA_CLASS_FILE 2 +#define AA_CLASS_CAP 3 +#define AA_CLASS_DEPRECATED 4 +#define AA_CLASS_RLIMITS 5 +#define AA_CLASS_DOMAIN 6 +#define AA_CLASS_MOUNT 7 +#define AA_CLASS_PTRACE 9 +#define AA_CLASS_SIGNAL 10 +#define AA_CLASS_NET 14 +#define AA_CLASS_LABEL 16 + +#define AA_CLASS_LAST AA_CLASS_LABEL + +/* Control parameters settable through module/boot flags */ +extern enum audit_mode aa_g_audit; +extern bool aa_g_audit_header; +extern bool aa_g_debug; +extern bool aa_g_hash_policy; +extern bool aa_g_export_binary; +extern int aa_g_rawdata_compression_level; +extern bool aa_g_lock_policy; +extern bool aa_g_logsyscall; +extern bool aa_g_paranoid_load; +extern unsigned int aa_g_path_max; + +#endif /* __APPARMOR_H */ diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h new file mode 100644 index 000000000..1e94904f6 --- /dev/null +++ b/security/apparmor/include/apparmorfs.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor filesystem definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_APPARMORFS_H +#define __AA_APPARMORFS_H + +extern struct path aa_null; + +enum aa_sfs_type { + AA_SFS_TYPE_BOOLEAN, + AA_SFS_TYPE_STRING, + AA_SFS_TYPE_U64, + AA_SFS_TYPE_FOPS, + AA_SFS_TYPE_DIR, +}; + +struct aa_sfs_entry; + +struct aa_sfs_entry { + const char *name; + struct dentry *dentry; + umode_t mode; + enum aa_sfs_type v_type; + union { + bool boolean; + char *string; + unsigned long u64; + struct aa_sfs_entry *files; + } v; + const struct file_operations *file_ops; +}; + +extern const struct file_operations aa_sfs_seq_file_ops; + +#define AA_SFS_FILE_BOOLEAN(_name, _value) \ + { .name = (_name), .mode = 0444, \ + .v_type = AA_SFS_TYPE_BOOLEAN, .v.boolean = (_value), \ + .file_ops = &aa_sfs_seq_file_ops } +#define AA_SFS_FILE_STRING(_name, _value) \ + { .name = (_name), .mode = 0444, \ + .v_type = AA_SFS_TYPE_STRING, .v.string = (_value), \ + .file_ops = &aa_sfs_seq_file_ops } +#define AA_SFS_FILE_U64(_name, _value) \ + { .name = (_name), .mode = 0444, \ + .v_type = AA_SFS_TYPE_U64, .v.u64 = (_value), \ + .file_ops = &aa_sfs_seq_file_ops } +#define AA_SFS_FILE_FOPS(_name, _mode, _fops) \ + { .name = (_name), .v_type = AA_SFS_TYPE_FOPS, \ + .mode = (_mode), .file_ops = (_fops) } +#define AA_SFS_DIR(_name, _value) \ + { .name = (_name), .v_type = AA_SFS_TYPE_DIR, .v.files = (_value) } + +extern void __init aa_destroy_aafs(void); + +struct aa_profile; +struct aa_ns; + +enum aafs_ns_type { + AAFS_NS_DIR, + AAFS_NS_PROFS, + AAFS_NS_NS, + AAFS_NS_RAW_DATA, + AAFS_NS_LOAD, + AAFS_NS_REPLACE, + AAFS_NS_REMOVE, + AAFS_NS_REVISION, + AAFS_NS_COUNT, + AAFS_NS_MAX_COUNT, + AAFS_NS_SIZE, + AAFS_NS_MAX_SIZE, + AAFS_NS_OWNER, + AAFS_NS_SIZEOF, +}; + +enum aafs_prof_type { + AAFS_PROF_DIR, + AAFS_PROF_PROFS, + AAFS_PROF_NAME, + AAFS_PROF_MODE, + AAFS_PROF_ATTACH, + AAFS_PROF_HASH, + AAFS_PROF_RAW_DATA, + AAFS_PROF_RAW_HASH, + AAFS_PROF_RAW_ABI, + AAFS_PROF_SIZEOF, +}; + +#define ns_dir(X) ((X)->dents[AAFS_NS_DIR]) +#define ns_subns_dir(X) ((X)->dents[AAFS_NS_NS]) +#define ns_subprofs_dir(X) ((X)->dents[AAFS_NS_PROFS]) +#define ns_subdata_dir(X) ((X)->dents[AAFS_NS_RAW_DATA]) +#define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) +#define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) +#define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) +#define ns_subrevision(X) ((X)->dents[AAFS_NS_REVISION]) + +#define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) +#define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) + +void __aa_bump_ns_revision(struct aa_ns *ns); +void __aafs_profile_rmdir(struct aa_profile *profile); +void __aafs_profile_migrate_dents(struct aa_profile *old, + struct aa_profile *new); +int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); +void __aafs_ns_rmdir(struct aa_ns *ns); +int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name, + struct dentry *dent); + +struct aa_loaddata; + +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY +void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); +int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); +#else +static inline void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata) +{ + /* empty stub */ +} + +static inline int __aa_fs_create_rawdata(struct aa_ns *ns, + struct aa_loaddata *rawdata) +{ + return 0; +} +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + +#endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h new file mode 100644 index 000000000..18519a4eb --- /dev/null +++ b/security/apparmor/include/audit.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor auditing function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_AUDIT_H +#define __AA_AUDIT_H + +#include <linux/audit.h> +#include <linux/fs.h> +#include <linux/lsm_audit.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include "file.h" +#include "label.h" + +extern const char *const audit_mode_names[]; +#define AUDIT_MAX_INDEX 5 +enum audit_mode { + AUDIT_NORMAL, /* follow normal auditing of accesses */ + AUDIT_QUIET_DENIED, /* quiet all denied access messages */ + AUDIT_QUIET, /* quiet all messages */ + AUDIT_NOQUIET, /* do not quiet audit messages */ + AUDIT_ALL /* audit all accesses */ +}; + +enum audit_type { + AUDIT_APPARMOR_AUDIT, + AUDIT_APPARMOR_ALLOWED, + AUDIT_APPARMOR_DENIED, + AUDIT_APPARMOR_HINT, + AUDIT_APPARMOR_STATUS, + AUDIT_APPARMOR_ERROR, + AUDIT_APPARMOR_KILL, + AUDIT_APPARMOR_AUTO +}; + +#define OP_NULL NULL + +#define OP_SYSCTL "sysctl" +#define OP_CAPABLE "capable" + +#define OP_UNLINK "unlink" +#define OP_MKDIR "mkdir" +#define OP_RMDIR "rmdir" +#define OP_MKNOD "mknod" +#define OP_TRUNC "truncate" +#define OP_LINK "link" +#define OP_SYMLINK "symlink" +#define OP_RENAME_SRC "rename_src" +#define OP_RENAME_DEST "rename_dest" +#define OP_CHMOD "chmod" +#define OP_CHOWN "chown" +#define OP_GETATTR "getattr" +#define OP_OPEN "open" + +#define OP_FRECEIVE "file_receive" +#define OP_FPERM "file_perm" +#define OP_FLOCK "file_lock" +#define OP_FMMAP "file_mmap" +#define OP_FMPROT "file_mprotect" +#define OP_INHERIT "file_inherit" + +#define OP_PIVOTROOT "pivotroot" +#define OP_MOUNT "mount" +#define OP_UMOUNT "umount" + +#define OP_CREATE "create" +#define OP_POST_CREATE "post_create" +#define OP_BIND "bind" +#define OP_CONNECT "connect" +#define OP_LISTEN "listen" +#define OP_ACCEPT "accept" +#define OP_SENDMSG "sendmsg" +#define OP_RECVMSG "recvmsg" +#define OP_GETSOCKNAME "getsockname" +#define OP_GETPEERNAME "getpeername" +#define OP_GETSOCKOPT "getsockopt" +#define OP_SETSOCKOPT "setsockopt" +#define OP_SHUTDOWN "socket_shutdown" + +#define OP_PTRACE "ptrace" +#define OP_SIGNAL "signal" + +#define OP_EXEC "exec" + +#define OP_CHANGE_HAT "change_hat" +#define OP_CHANGE_PROFILE "change_profile" +#define OP_CHANGE_ONEXEC "change_onexec" +#define OP_STACK "stack" +#define OP_STACK_ONEXEC "stack_onexec" + +#define OP_SETPROCATTR "setprocattr" +#define OP_SETRLIMIT "setrlimit" + +#define OP_PROF_REPL "profile_replace" +#define OP_PROF_LOAD "profile_load" +#define OP_PROF_RM "profile_remove" + + +struct apparmor_audit_data { + int error; + int type; + const char *op; + struct aa_label *label; + const char *name; + const char *info; + u32 request; + u32 denied; + union { + /* these entries require a custom callback fn */ + struct { + struct aa_label *peer; + union { + struct { + const char *target; + kuid_t ouid; + } fs; + struct { + int rlim; + unsigned long max; + } rlim; + struct { + int signal; + int unmappedsig; + }; + struct { + int type, protocol; + struct sock *peer_sk; + void *addr; + int addrlen; + } net; + }; + }; + struct { + struct aa_profile *profile; + const char *ns; + long pos; + } iface; + struct { + const char *src_name; + const char *type; + const char *trans; + const char *data; + unsigned long flags; + } mnt; + }; +}; + +/* macros for dealing with apparmor_audit_data structure */ +#define aad(SA) ((SA)->apparmor_audit_data) +#define DEFINE_AUDIT_DATA(NAME, T, X) \ + /* TODO: cleanup audit init so we don't need _aad = {0,} */ \ + struct apparmor_audit_data NAME ## _aad = { .op = (X), }; \ + struct common_audit_data NAME = \ + { \ + .type = (T), \ + .u.tsk = NULL, \ + }; \ + NAME.apparmor_audit_data = &(NAME ## _aad) + +void aa_audit_msg(int type, struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)); +int aa_audit(int type, struct aa_profile *profile, struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)); + +#define aa_audit_error(ERROR, SA, CB) \ +({ \ + aad((SA))->error = (ERROR); \ + aa_audit_msg(AUDIT_APPARMOR_ERROR, (SA), (CB)); \ + aad((SA))->error; \ +}) + + +static inline int complain_error(int error) +{ + if (error == -EPERM || error == -EACCES) + return 0; + return error; +} + +void aa_audit_rule_free(void *vrule); +int aa_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule); +int aa_audit_rule_known(struct audit_krule *rule); +int aa_audit_rule_match(u32 sid, u32 field, u32 op, void *vrule); + +#endif /* __AA_AUDIT_H */ diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h new file mode 100644 index 000000000..d420e2d10 --- /dev/null +++ b/security/apparmor/include/capability.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor capability mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2013 Canonical Ltd. + */ + +#ifndef __AA_CAPABILITY_H +#define __AA_CAPABILITY_H + +#include <linux/sched.h> + +#include "apparmorfs.h" + +struct aa_label; + +/* aa_caps - confinement data for capabilities + * @allowed: capabilities mask + * @audit: caps that are to be audited + * @denied: caps that are explicitly denied + * @quiet: caps that should not be audited + * @kill: caps that when requested will result in the task being killed + * @extended: caps that are subject finer grained mediation + */ +struct aa_caps { + kernel_cap_t allow; + kernel_cap_t audit; + kernel_cap_t denied; + kernel_cap_t quiet; + kernel_cap_t kill; + kernel_cap_t extended; +}; + +extern struct aa_sfs_entry aa_sfs_entry_caps[]; + +int aa_capable(struct aa_label *label, int cap, unsigned int opts); + +static inline void aa_free_cap_rules(struct aa_caps *caps) +{ + /* NOP */ +} + +#endif /* __AA_CAPBILITY_H */ diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h new file mode 100644 index 000000000..0b9ae4804 --- /dev/null +++ b/security/apparmor/include/cred.h @@ -0,0 +1,188 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor contexts used to associate "labels" to objects. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_CONTEXT_H +#define __AA_CONTEXT_H + +#include <linux/cred.h> +#include <linux/slab.h> +#include <linux/sched.h> + +#include "label.h" +#include "policy_ns.h" +#include "task.h" + +static inline struct aa_label *cred_label(const struct cred *cred) +{ + struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred; + + AA_BUG(!blob); + return *blob; +} + +static inline void set_cred_label(const struct cred *cred, + struct aa_label *label) +{ + struct aa_label **blob = cred->security + apparmor_blob_sizes.lbs_cred; + + AA_BUG(!blob); + *blob = label; +} + +/** + * aa_cred_raw_label - obtain cred's label + * @cred: cred to obtain label from (NOT NULL) + * + * Returns: confining label + * + * does NOT increment reference count + */ +static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) +{ + struct aa_label *label = cred_label(cred); + + AA_BUG(!label); + return label; +} + +/** + * aa_get_newest_cred_label - obtain the newest label on a cred + * @cred: cred to obtain label from (NOT NULL) + * + * Returns: newest version of confining label + */ +static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) +{ + return aa_get_newest_label(aa_cred_raw_label(cred)); +} + +/** + * __aa_task_raw_label - retrieve another task's label + * @task: task to query (NOT NULL) + * + * Returns: @task's label without incrementing its ref count + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline struct aa_label *__aa_task_raw_label(struct task_struct *task) +{ + return aa_cred_raw_label(__task_cred(task)); +} + +/** + * aa_current_raw_label - find the current tasks confining label + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * This fn will not update the tasks cred to the most up to date version + * of the label so it is safe to call when inside of locks. + */ +static inline struct aa_label *aa_current_raw_label(void) +{ + return aa_cred_raw_label(current_cred()); +} + +/** + * aa_get_current_label - get the newest version of the current tasks label + * + * Returns: newest version of confining label (NOT NULL) + * + * This fn will not update the tasks cred, so it is safe inside of locks + * + * The returned reference must be put with aa_put_label() + */ +static inline struct aa_label *aa_get_current_label(void) +{ + struct aa_label *l = aa_current_raw_label(); + + if (label_is_stale(l)) + return aa_get_newest_label(l); + return aa_get_label(l); +} + +#define __end_current_label_crit_section(X) end_current_label_crit_section(X) + +/** + * end_label_crit_section - put a reference found with begin_current_label.. + * @label: label reference to put + * + * Should only be used with a reference obtained with + * begin_current_label_crit_section and never used in situations where the + * task cred may be updated + */ +static inline void end_current_label_crit_section(struct aa_label *label) +{ + if (label != aa_current_raw_label()) + aa_put_label(label); +} + +/** + * __begin_current_label_crit_section - current's confining label + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * safe to call inside locks + * + * The returned reference must be put with __end_current_label_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between __begin_current_label_crit_section() .. + * __end_current_label_crit_section() + */ +static inline struct aa_label *__begin_current_label_crit_section(void) +{ + struct aa_label *label = aa_current_raw_label(); + + if (label_is_stale(label)) + label = aa_get_newest_label(label); + + return label; +} + +/** + * begin_current_label_crit_section - current's confining label and update it + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * Not safe to call inside locks + * + * The returned reference must be put with end_current_label_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between begin_current_label_crit_section() .. + * end_current_label_crit_section() + */ +static inline struct aa_label *begin_current_label_crit_section(void) +{ + struct aa_label *label = aa_current_raw_label(); + + might_sleep(); + + if (label_is_stale(label)) { + label = aa_get_newest_label(label); + if (aa_replace_current_label(label) == 0) + /* task cred will keep the reference */ + aa_put_label(label); + } + + return label; +} + +static inline struct aa_ns *aa_get_current_ns(void) +{ + struct aa_label *label; + struct aa_ns *ns; + + label = __begin_current_label_crit_section(); + ns = aa_get_ns(labels_ns(label)); + __end_current_label_crit_section(label); + + return ns; +} + +#endif /* __AA_CONTEXT_H */ diff --git a/security/apparmor/include/crypto.h b/security/apparmor/include/crypto.h new file mode 100644 index 000000000..636a04e20 --- /dev/null +++ b/security/apparmor/include/crypto.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor policy loading interface function definitions. + * + * Copyright 2013 Canonical Ltd. + */ + +#ifndef __APPARMOR_CRYPTO_H +#define __APPARMOR_CRYPTO_H + +#include "policy.h" + +#ifdef CONFIG_SECURITY_APPARMOR_HASH +unsigned int aa_hash_size(void); +char *aa_calc_hash(void *data, size_t len); +int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, + size_t len); +#else +static inline char *aa_calc_hash(void *data, size_t len) +{ + return NULL; +} +static inline int aa_calc_profile_hash(struct aa_profile *profile, u32 version, + void *start, size_t len) +{ + return 0; +} + +static inline unsigned int aa_hash_size(void) +{ + return 0; +} +#endif + +#endif /* __APPARMOR_CRYPTO_H */ diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h new file mode 100644 index 000000000..d14928fe1 --- /dev/null +++ b/security/apparmor/include/domain.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor security domain transition function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#include <linux/binfmts.h> +#include <linux/types.h> + +#include "label.h" + +#ifndef __AA_DOMAIN_H +#define __AA_DOMAIN_H + +struct aa_domain { + int size; + char **table; +}; + +#define AA_CHANGE_NOFLAGS 0 +#define AA_CHANGE_TEST 1 +#define AA_CHANGE_CHILD 2 +#define AA_CHANGE_ONEXEC 4 +#define AA_CHANGE_STACK 8 + +struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, + const char **name); + +int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm); + +void aa_free_domain_entries(struct aa_domain *domain); +int aa_change_hat(const char *hats[], int count, u64 token, int flags); +int aa_change_profile(const char *fqname, int flags); + +#endif /* __AA_DOMAIN_H */ diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h new file mode 100644 index 000000000..029cb20e3 --- /dev/null +++ b/security/apparmor/include/file.h @@ -0,0 +1,240 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_FILE_H +#define __AA_FILE_H + +#include <linux/spinlock.h> + +#include "domain.h" +#include "match.h" +#include "perms.h" + +struct aa_profile; +struct path; + +#define mask_mode_t(X) (X & (MAY_EXEC | MAY_WRITE | MAY_READ | MAY_APPEND)) + +#define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ + AA_MAY_CREATE | AA_MAY_DELETE | \ + AA_MAY_GETATTR | AA_MAY_SETATTR | \ + AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ + AA_EXEC_MMAP | AA_MAY_LINK) + +static inline struct aa_file_ctx *file_ctx(struct file *file) +{ + return file->f_security + apparmor_blob_sizes.lbs_file; +} + +/* struct aa_file_ctx - the AppArmor context the file was opened in + * @lock: lock to update the ctx + * @label: label currently cached on the ctx + * @perms: the permission the file was opened with + */ +struct aa_file_ctx { + spinlock_t lock; + struct aa_label __rcu *label; + u32 allow; +}; + +/** + * aa_alloc_file_ctx - allocate file_ctx + * @label: initial label of task creating the file + * @gfp: gfp flags for allocation + * + * Returns: file_ctx or NULL on failure + */ +static inline struct aa_file_ctx *aa_alloc_file_ctx(struct aa_label *label, + gfp_t gfp) +{ + struct aa_file_ctx *ctx; + + ctx = kzalloc(sizeof(struct aa_file_ctx), gfp); + if (ctx) { + spin_lock_init(&ctx->lock); + rcu_assign_pointer(ctx->label, aa_get_label(label)); + } + return ctx; +} + +/** + * aa_free_file_ctx - free a file_ctx + * @ctx: file_ctx to free (MAYBE_NULL) + */ +static inline void aa_free_file_ctx(struct aa_file_ctx *ctx) +{ + if (ctx) { + aa_put_label(rcu_access_pointer(ctx->label)); + kfree_sensitive(ctx); + } +} + +static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx) +{ + return aa_get_label_rcu(&ctx->label); +} + +/* + * The xindex is broken into 3 parts + * - index - an index into either the exec name table or the variable table + * - exec type - which determines how the executable name and index are used + * - flags - which modify how the destination name is applied + */ +#define AA_X_INDEX_MASK 0x03ff + +#define AA_X_TYPE_MASK 0x0c00 +#define AA_X_TYPE_SHIFT 10 +#define AA_X_NONE 0x0000 +#define AA_X_NAME 0x0400 /* use executable name px */ +#define AA_X_TABLE 0x0800 /* use a specified name ->n# */ + +#define AA_X_UNSAFE 0x1000 +#define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ +#define AA_X_INHERIT 0x4000 +#define AA_X_UNCONFINED 0x8000 + +/* need to make conditional which ones are being set */ +struct path_cond { + kuid_t uid; + umode_t mode; +}; + +#define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) + +/* FIXME: split perms from dfa and match this to description + * also add delegation info. + */ +static inline u16 dfa_map_xindex(u16 mask) +{ + u16 old_index = (mask >> 10) & 0xf; + u16 index = 0; + + if (mask & 0x100) + index |= AA_X_UNSAFE; + if (mask & 0x200) + index |= AA_X_INHERIT; + if (mask & 0x80) + index |= AA_X_UNCONFINED; + + if (old_index == 1) { + index |= AA_X_UNCONFINED; + } else if (old_index == 2) { + index |= AA_X_NAME; + } else if (old_index == 3) { + index |= AA_X_NAME | AA_X_CHILD; + } else if (old_index) { + index |= AA_X_TABLE; + index |= old_index - 4; + } + + return index; +} + +/* + * map old dfa inline permissions to new format + */ +#define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_user_xbits(dfa, state) (((ACCEPT_TABLE(dfa)[state]) >> 7) & 0x7f) +#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) +#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) +#define dfa_user_xindex(dfa, state) \ + (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) + +#define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ + 0x7f) | \ + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_other_xbits(dfa, state) \ + ((((ACCEPT_TABLE(dfa)[state]) >> 7) >> 14) & 0x7f) +#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) +#define dfa_other_quiet(dfa, state) \ + ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) +#define dfa_other_xindex(dfa, state) \ + dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) + +int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, + const char *op, u32 request, const char *name, + const char *target, struct aa_label *tlabel, kuid_t ouid, + const char *info, int error); + +/** + * struct aa_file_rules - components used for file rule permissions + * @dfa: dfa to match path names and conditionals against + * @perms: permission table indexed by the matched state accept entry of @dfa + * @trans: transition table for indexed by named x transitions + * + * File permission are determined by matching a path against @dfa and + * then using the value of the accept entry for the matching state as + * an index into @perms. If a named exec transition is required it is + * looked up in the transition table. + */ +struct aa_file_rules { + unsigned int start; + struct aa_dfa *dfa; + /* struct perms perms; */ + struct aa_domain trans; + /* TODO: add delegate table */ +}; + +struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state, + struct path_cond *cond); +unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, + const char *name, struct path_cond *cond, + struct aa_perms *perms); + +int __aa_path_perm(const char *op, struct aa_profile *profile, + const char *name, u32 request, struct path_cond *cond, + int flags, struct aa_perms *perms); +int aa_path_perm(const char *op, struct aa_label *label, + const struct path *path, int flags, u32 request, + struct path_cond *cond); + +int aa_path_link(struct aa_label *label, struct dentry *old_dentry, + const struct path *new_dir, struct dentry *new_dentry); + +int aa_file_perm(const char *op, struct aa_label *label, struct file *file, + u32 request, bool in_atomic); + +void aa_inherit_files(const struct cred *cred, struct files_struct *files); + +static inline void aa_free_file_rules(struct aa_file_rules *rules) +{ + aa_put_dfa(rules->dfa); + aa_free_domain_entries(&rules->trans); +} + +/** + * aa_map_file_perms - map file flags to AppArmor permissions + * @file: open file to map flags to AppArmor permissions + * + * Returns: apparmor permission set for the file + */ +static inline u32 aa_map_file_to_perms(struct file *file) +{ + int flags = file->f_flags; + u32 perms = 0; + + if (file->f_mode & FMODE_WRITE) + perms |= MAY_WRITE; + if (file->f_mode & FMODE_READ) + perms |= MAY_READ; + + if ((flags & O_APPEND) && (perms & MAY_WRITE)) + perms = (perms & ~MAY_WRITE) | MAY_APPEND; + /* trunc implies write permission */ + if (flags & O_TRUNC) + perms |= MAY_WRITE; + if (flags & O_CREAT) + perms |= AA_MAY_CREATE; + + return perms; +} + +#endif /* __AA_FILE_H */ diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h new file mode 100644 index 000000000..a1ac6ffb9 --- /dev/null +++ b/security/apparmor/include/ipc.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor ipc mediation function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + */ + +#ifndef __AA_IPC_H +#define __AA_IPC_H + +#include <linux/sched.h> + +int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); + +#endif /* __AA_IPC_H */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h new file mode 100644 index 000000000..860484c6f --- /dev/null +++ b/security/apparmor/include/label.h @@ -0,0 +1,467 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor label definitions + * + * Copyright 2017 Canonical Ltd. + */ + +#ifndef __AA_LABEL_H +#define __AA_LABEL_H + +#include <linux/atomic.h> +#include <linux/audit.h> +#include <linux/rbtree.h> +#include <linux/rcupdate.h> + +#include "apparmor.h" +#include "lib.h" + +struct aa_ns; + +#define LOCAL_VEC_ENTRIES 8 +#define DEFINE_VEC(T, V) \ + struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES]; \ + struct aa_ ## T **(V) + +#define vec_setup(T, V, N, GFP) \ +({ \ + if ((N) <= LOCAL_VEC_ENTRIES) { \ + typeof(N) i; \ + (V) = (_ ## V ## _localtmp); \ + for (i = 0; i < (N); i++) \ + (V)[i] = NULL; \ + } else \ + (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP)); \ + (V) ? 0 : -ENOMEM; \ +}) + +#define vec_cleanup(T, V, N) \ +do { \ + int i; \ + for (i = 0; i < (N); i++) { \ + if (!IS_ERR_OR_NULL((V)[i])) \ + aa_put_ ## T((V)[i]); \ + } \ + if ((V) != _ ## V ## _localtmp) \ + kfree(V); \ +} while (0) + +#define vec_last(VEC, SIZE) ((VEC)[(SIZE) - 1]) +#define vec_ns(VEC, SIZE) (vec_last((VEC), (SIZE))->ns) +#define vec_labelset(VEC, SIZE) (&vec_ns((VEC), (SIZE))->labels) +#define cleanup_domain_vec(V, L) cleanup_label_vec((V), (L)->size) + +struct aa_profile; +#define VEC_FLAG_TERMINATE 1 +int aa_vec_unique(struct aa_profile **vec, int n, int flags); +struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len, + gfp_t gfp); +#define aa_sort_and_merge_vec(N, V) \ + aa_sort_and_merge_profiles((N), (struct aa_profile **)(V)) + + +/* struct aa_labelset - set of labels for a namespace + * + * Labels are reference counted; aa_labelset does not contribute to label + * reference counts. Once a label's last refcount is put it is removed from + * the set. + */ +struct aa_labelset { + rwlock_t lock; + + struct rb_root root; +}; + +#define __labelset_for_each(LS, N) \ + for ((N) = rb_first(&(LS)->root); (N); (N) = rb_next(N)) + +enum label_flags { + FLAG_HAT = 1, /* profile is a hat */ + FLAG_UNCONFINED = 2, /* label unconfined only if all */ + FLAG_NULL = 4, /* profile is null learning profile */ + FLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ + FLAG_IMMUTIBLE = 0x10, /* don't allow changes/replacement */ + FLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ + FLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ + FLAG_NS_COUNT = 0x80, /* carries NS ref count */ + FLAG_IN_TREE = 0x100, /* label is in tree */ + FLAG_PROFILE = 0x200, /* label is a profile */ + FLAG_EXPLICIT = 0x400, /* explicit static label */ + FLAG_STALE = 0x800, /* replaced/removed */ + FLAG_RENAMED = 0x1000, /* label has renaming in it */ + FLAG_REVOKED = 0x2000, /* label has revocation in it */ + FLAG_DEBUG1 = 0x4000, + FLAG_DEBUG2 = 0x8000, + + /* These flags must correspond with PATH_flags */ + /* TODO: add new path flags */ +}; + +struct aa_label; +struct aa_proxy { + struct kref count; + struct aa_label __rcu *label; +}; + +struct label_it { + int i, j; +}; + +/* struct aa_label - lazy labeling struct + * @count: ref count of active users + * @node: rbtree position + * @rcu: rcu callback struct + * @proxy: is set to the label that replaced this label + * @hname: text representation of the label (MAYBE_NULL) + * @flags: stale and other flags - values may change under label set lock + * @secid: secid that references this label + * @size: number of entries in @ent[] + * @ent: set of profiles for label, actual size determined by @size + */ +struct aa_label { + struct kref count; + struct rb_node node; + struct rcu_head rcu; + struct aa_proxy *proxy; + __counted char *hname; + long flags; + u32 secid; + int size; + struct aa_profile *vec[]; +}; + +#define last_error(E, FN) \ +do { \ + int __subE = (FN); \ + if (__subE) \ + (E) = __subE; \ +} while (0) + +#define label_isprofile(X) ((X)->flags & FLAG_PROFILE) +#define label_unconfined(X) ((X)->flags & FLAG_UNCONFINED) +#define unconfined(X) label_unconfined(X) +#define label_is_stale(X) ((X)->flags & FLAG_STALE) +#define __label_make_stale(X) ((X)->flags |= FLAG_STALE) +#define labels_ns(X) (vec_ns(&((X)->vec[0]), (X)->size)) +#define labels_set(X) (&labels_ns(X)->labels) +#define labels_view(X) labels_ns(X) +#define labels_profile(X) ((X)->vec[(X)->size - 1]) + + +int aa_label_next_confined(struct aa_label *l, int i); + +/* for each profile in a label */ +#define label_for_each(I, L, P) \ + for ((I).i = 0; ((P) = (L)->vec[(I).i]); ++((I).i)) + +/* assumes break/goto ended label_for_each */ +#define label_for_each_cont(I, L, P) \ + for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i)) + +#define next_comb(I, L1, L2) \ +do { \ + (I).j++; \ + if ((I).j >= (L2)->size) { \ + (I).i++; \ + (I).j = 0; \ + } \ +} while (0) + + +/* for each combination of P1 in L1, and P2 in L2 */ +#define label_for_each_comb(I, L1, L2, P1, P2) \ +for ((I).i = (I).j = 0; \ + ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \ + (I) = next_comb(I, L1, L2)) + +#define fn_for_each_comb(L1, L2, P1, P2, FN) \ +({ \ + struct label_it i; \ + int __E = 0; \ + label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + +/* for each profile that is enforcing confinement in a label */ +#define label_for_each_confined(I, L, P) \ + for ((I).i = aa_label_next_confined((L), 0); \ + ((P) = (L)->vec[(I).i]); \ + (I).i = aa_label_next_confined((L), (I).i + 1)) + +#define label_for_each_in_merge(I, A, B, P) \ + for ((I).i = (I).j = 0; \ + ((P) = aa_label_next_in_merge(&(I), (A), (B))); \ + ) + +#define label_for_each_not_in_set(I, SET, SUB, P) \ + for ((I).i = (I).j = 0; \ + ((P) = __aa_label_next_not_in_set(&(I), (SET), (SUB))); \ + ) + +#define next_in_ns(i, NS, L) \ +({ \ + typeof(i) ___i = (i); \ + while ((L)->vec[___i] && (L)->vec[___i]->ns != (NS)) \ + (___i)++; \ + (___i); \ +}) + +#define label_for_each_in_ns(I, NS, L, P) \ + for ((I).i = next_in_ns(0, (NS), (L)); \ + ((P) = (L)->vec[(I).i]); \ + (I).i = next_in_ns((I).i + 1, (NS), (L))) + +#define fn_for_each_in_ns(L, P, FN) \ +({ \ + struct label_it __i; \ + struct aa_ns *__ns = labels_ns(L); \ + int __E = 0; \ + label_for_each_in_ns(__i, __ns, (L), (P)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + + +#define fn_for_each_XXX(L, P, FN, ...) \ +({ \ + struct label_it i; \ + int __E = 0; \ + label_for_each ## __VA_ARGS__(i, (L), (P)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + +#define fn_for_each(L, P, FN) fn_for_each_XXX(L, P, FN) +#define fn_for_each_confined(L, P, FN) fn_for_each_XXX(L, P, FN, _confined) + +#define fn_for_each2_XXX(L1, L2, P, FN, ...) \ +({ \ + struct label_it i; \ + int __E = 0; \ + label_for_each ## __VA_ARGS__(i, (L1), (L2), (P)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + +#define fn_for_each_in_merge(L1, L2, P, FN) \ + fn_for_each2_XXX((L1), (L2), P, FN, _in_merge) +#define fn_for_each_not_in_set(L1, L2, P, FN) \ + fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set) + +#define LABEL_MEDIATES(L, C) \ +({ \ + struct aa_profile *profile; \ + struct label_it i; \ + int ret = 0; \ + label_for_each(i, (L), profile) { \ + if (PROFILE_MEDIATES(profile, (C))) { \ + ret = 1; \ + break; \ + } \ + } \ + ret; \ +}) + + +void aa_labelset_destroy(struct aa_labelset *ls); +void aa_labelset_init(struct aa_labelset *ls); +void __aa_labelset_update_subtree(struct aa_ns *ns); + +void aa_label_destroy(struct aa_label *label); +void aa_label_free(struct aa_label *label); +void aa_label_kref(struct kref *kref); +bool aa_label_init(struct aa_label *label, int size, gfp_t gfp); +struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp); + +bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub); +bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub); +struct aa_profile *__aa_label_next_not_in_set(struct label_it *I, + struct aa_label *set, + struct aa_label *sub); +bool aa_label_remove(struct aa_label *label); +struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *l); +bool aa_label_replace(struct aa_label *old, struct aa_label *new); +bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old, + struct aa_label *new); + +struct aa_label *aa_label_find(struct aa_label *l); + +struct aa_profile *aa_label_next_in_merge(struct label_it *I, + struct aa_label *a, + struct aa_label *b); +struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b); +struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b, + gfp_t gfp); + + +bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp); + +#define FLAGS_NONE 0 +#define FLAG_SHOW_MODE 1 +#define FLAG_VIEW_SUBNS 2 +#define FLAG_HIDDEN_UNCONFINED 4 +#define FLAG_ABS_ROOT 8 +int aa_label_snxprint(char *str, size_t size, struct aa_ns *view, + struct aa_label *label, int flags); +int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label, + int flags, gfp_t gfp); +int aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns, + struct aa_label *label, int flags, gfp_t gfp); +void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, + struct aa_label *label, int flags, gfp_t gfp); +void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, + struct aa_label *label, int flags, gfp_t gfp); +void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, + gfp_t gfp); +void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp); +void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp); +void aa_label_printk(struct aa_label *label, gfp_t gfp); + +struct aa_label *aa_label_strn_parse(struct aa_label *base, const char *str, + size_t n, gfp_t gfp, bool create, + bool force_stack); +struct aa_label *aa_label_parse(struct aa_label *base, const char *str, + gfp_t gfp, bool create, bool force_stack); + +static inline const char *aa_label_strn_split(const char *str, int n) +{ + const char *pos; + unsigned int state; + + state = aa_dfa_matchn_until(stacksplitdfa, DFA_START, str, n, &pos); + if (!ACCEPT_TABLE(stacksplitdfa)[state]) + return NULL; + + return pos - 3; +} + +static inline const char *aa_label_str_split(const char *str) +{ + const char *pos; + unsigned int state; + + state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos); + if (!ACCEPT_TABLE(stacksplitdfa)[state]) + return NULL; + + return pos - 3; +} + + + +struct aa_perms; +int aa_label_match(struct aa_profile *profile, struct aa_label *label, + unsigned int state, bool subns, u32 request, + struct aa_perms *perms); + + +/** + * __aa_get_label - get a reference count to uncounted label reference + * @l: reference to get a count on + * + * Returns: pointer to reference OR NULL if race is lost and reference is + * being repeated. + * Requires: lock held, and the return code MUST be checked + */ +static inline struct aa_label *__aa_get_label(struct aa_label *l) +{ + if (l && kref_get_unless_zero(&l->count)) + return l; + + return NULL; +} + +static inline struct aa_label *aa_get_label(struct aa_label *l) +{ + if (l) + kref_get(&(l->count)); + + return l; +} + + +/** + * aa_get_label_rcu - increment refcount on a label that can be replaced + * @l: pointer to label that can be replaced (NOT NULL) + * + * Returns: pointer to a refcounted label. + * else NULL if no label + */ +static inline struct aa_label *aa_get_label_rcu(struct aa_label __rcu **l) +{ + struct aa_label *c; + + rcu_read_lock(); + do { + c = rcu_dereference(*l); + } while (c && !kref_get_unless_zero(&c->count)); + rcu_read_unlock(); + + return c; +} + +/** + * aa_get_newest_label - find the newest version of @l + * @l: the label to check for newer versions of + * + * Returns: refcounted newest version of @l taking into account + * replacement, renames and removals + * return @l. + */ +static inline struct aa_label *aa_get_newest_label(struct aa_label *l) +{ + if (!l) + return NULL; + + if (label_is_stale(l)) { + struct aa_label *tmp; + + AA_BUG(!l->proxy); + AA_BUG(!l->proxy->label); + /* BUG: only way this can happen is @l ref count and its + * replacement count have gone to 0 and are on their way + * to destruction. ie. we have a refcounting error + */ + tmp = aa_get_label_rcu(&l->proxy->label); + AA_BUG(!tmp); + + return tmp; + } + + return aa_get_label(l); +} + +static inline void aa_put_label(struct aa_label *l) +{ + if (l) + kref_put(&l->count, aa_label_kref); +} + + +struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp); +void aa_proxy_kref(struct kref *kref); + +static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy) +{ + if (proxy) + kref_get(&(proxy->count)); + + return proxy; +} + +static inline void aa_put_proxy(struct aa_proxy *proxy) +{ + if (proxy) + kref_put(&proxy->count, aa_proxy_kref); +} + +void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new); + +#endif /* __AA_LABEL_H */ diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h new file mode 100644 index 000000000..f42359f58 --- /dev/null +++ b/security/apparmor/include/lib.h @@ -0,0 +1,298 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor lib definitions + * + * 2017 Canonical Ltd. + */ + +#ifndef __AA_LIB_H +#define __AA_LIB_H + +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/lsm_hooks.h> + +#include "match.h" + +/* + * DEBUG remains global (no per profile flag) since it is mostly used in sysctl + * which is not related to profile accesses. + */ + +#define DEBUG_ON (aa_g_debug) +/* + * split individual debug cases out in preparation for finer grained + * debug controls in the future. + */ +#define AA_DEBUG_LABEL DEBUG_ON +#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) +#define AA_DEBUG(fmt, args...) \ + do { \ + if (DEBUG_ON) \ + pr_debug_ratelimited("AppArmor: " fmt, ##args); \ + } while (0) + +#define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X) + +#define AA_BUG(X, args...) \ + do { \ + _Pragma("GCC diagnostic ignored \"-Wformat-zero-length\""); \ + AA_BUG_FMT((X), "" args); \ + _Pragma("GCC diagnostic warning \"-Wformat-zero-length\""); \ + } while (0) +#ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS +#define AA_BUG_FMT(X, fmt, args...) \ + WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args) +#else +#define AA_BUG_FMT(X, fmt, args...) no_printk(fmt, ##args) +#endif + +#define AA_ERROR(fmt, args...) \ + pr_err_ratelimited("AppArmor: " fmt, ##args) + +/* Flag indicating whether initialization completed */ +extern int apparmor_initialized; + +/* fn's in lib */ +const char *skipn_spaces(const char *str, size_t n); +char *aa_split_fqname(char *args, char **ns_name); +const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, + size_t *ns_len); +void aa_info_message(const char *str); + +/* Security blob offsets */ +extern struct lsm_blob_sizes apparmor_blob_sizes; + +/** + * aa_strneq - compare null terminated @str to a non null terminated substring + * @str: a null terminated string + * @sub: a substring, not necessarily null terminated + * @len: length of @sub to compare + * + * The @str string must be full consumed for this to be considered a match + */ +static inline bool aa_strneq(const char *str, const char *sub, int len) +{ + return !strncmp(str, sub, len) && !str[len]; +} + +/** + * aa_dfa_null_transition - step to next state after null character + * @dfa: the dfa to match against + * @start: the state of the dfa to start matching in + * + * aa_dfa_null_transition transitions to the next state after a null + * character which is not used in standard matching and is only + * used to separate pairs. + */ +static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, + unsigned int start) +{ + /* the null transition only needs the string's null terminator byte */ + return aa_dfa_next(dfa, start, 0); +} + +static inline bool path_mediated_fs(struct dentry *dentry) +{ + return !(dentry->d_sb->s_flags & SB_NOUSER); +} + + +struct counted_str { + struct kref count; + char name[]; +}; + +#define str_to_counted(str) \ + ((struct counted_str *)(str - offsetof(struct counted_str, name))) + +#define __counted /* atm just a notation */ + +void aa_str_kref(struct kref *kref); +char *aa_str_alloc(int size, gfp_t gfp); + + +static inline __counted char *aa_get_str(__counted char *str) +{ + if (str) + kref_get(&(str_to_counted(str)->count)); + + return str; +} + +static inline void aa_put_str(__counted char *str) +{ + if (str) + kref_put(&str_to_counted(str)->count, aa_str_kref); +} + + +/* struct aa_policy - common part of both namespaces and profiles + * @name: name of the object + * @hname - The hierarchical name + * @list: list policy object is on + * @profiles: head of the profiles list contained in the object + */ +struct aa_policy { + const char *name; + __counted char *hname; + struct list_head list; + struct list_head profiles; +}; + +/** + * basename - find the last component of an hname + * @name: hname to find the base profile name component of (NOT NULL) + * + * Returns: the tail (base profile name) name component of an hname + */ +static inline const char *basename(const char *hname) +{ + char *split; + + hname = strim((char *)hname); + for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) + hname = split + 2; + + return hname; +} + +/** + * __policy_find - find a policy by @name on a policy list + * @head: list to search (NOT NULL) + * @name: name to search for (NOT NULL) + * + * Requires: rcu_read_lock be held + * + * Returns: unrefcounted policy that match @name or NULL if not found + */ +static inline struct aa_policy *__policy_find(struct list_head *head, + const char *name) +{ + struct aa_policy *policy; + + list_for_each_entry_rcu(policy, head, list) { + if (!strcmp(policy->name, name)) + return policy; + } + return NULL; +} + +/** + * __policy_strn_find - find a policy that's name matches @len chars of @str + * @head: list to search (NOT NULL) + * @str: string to search for (NOT NULL) + * @len: length of match required + * + * Requires: rcu_read_lock be held + * + * Returns: unrefcounted policy that match @str or NULL if not found + * + * if @len == strlen(@strlen) then this is equiv to __policy_find + * other wise it allows searching for policy by a partial match of name + */ +static inline struct aa_policy *__policy_strn_find(struct list_head *head, + const char *str, int len) +{ + struct aa_policy *policy; + + list_for_each_entry_rcu(policy, head, list) { + if (aa_strneq(policy->name, str, len)) + return policy; + } + + return NULL; +} + +bool aa_policy_init(struct aa_policy *policy, const char *prefix, + const char *name, gfp_t gfp); +void aa_policy_destroy(struct aa_policy *policy); + + +/* + * fn_label_build - abstract out the build of a label transition + * @L: label the transition is being computed for + * @P: profile parameter derived from L by this macro, can be passed to FN + * @GFP: memory allocation type to use + * @FN: fn to call for each profile transition. @P is set to the profile + * + * Returns: new label on success + * ERR_PTR if build @FN fails + * NULL if label_build fails due to low memory conditions + * + * @FN must return a label or ERR_PTR on failure. NULL is not allowed + */ +#define fn_label_build(L, P, GFP, FN) \ +({ \ + __label__ __cleanup, __done; \ + struct aa_label *__new_; \ + \ + if ((L)->size > 1) { \ + /* TODO: add cache of transitions already done */ \ + struct label_it __i; \ + int __j, __k, __count; \ + DEFINE_VEC(label, __lvec); \ + DEFINE_VEC(profile, __pvec); \ + if (vec_setup(label, __lvec, (L)->size, (GFP))) { \ + __new_ = NULL; \ + goto __done; \ + } \ + __j = 0; \ + label_for_each(__i, (L), (P)) { \ + __new_ = (FN); \ + AA_BUG(!__new_); \ + if (IS_ERR(__new_)) \ + goto __cleanup; \ + __lvec[__j++] = __new_; \ + } \ + for (__j = __count = 0; __j < (L)->size; __j++) \ + __count += __lvec[__j]->size; \ + if (!vec_setup(profile, __pvec, __count, (GFP))) { \ + for (__j = __k = 0; __j < (L)->size; __j++) { \ + label_for_each(__i, __lvec[__j], (P)) \ + __pvec[__k++] = aa_get_profile(P); \ + } \ + __count -= aa_vec_unique(__pvec, __count, 0); \ + if (__count > 1) { \ + __new_ = aa_vec_find_or_create_label(__pvec,\ + __count, (GFP)); \ + /* only fails if out of Mem */ \ + if (!__new_) \ + __new_ = NULL; \ + } else \ + __new_ = aa_get_label(&__pvec[0]->label); \ + vec_cleanup(profile, __pvec, __count); \ + } else \ + __new_ = NULL; \ +__cleanup: \ + vec_cleanup(label, __lvec, (L)->size); \ + } else { \ + (P) = labels_profile(L); \ + __new_ = (FN); \ + } \ +__done: \ + if (!__new_) \ + AA_DEBUG("label build failed\n"); \ + (__new_); \ +}) + + +#define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \ +({ \ + struct aa_label *__new; \ + if ((P)->ns != (NS)) \ + __new = (OTHER_FN); \ + else \ + __new = (NS_FN); \ + (__new); \ +}) + +#define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \ +({ \ + fn_label_build((L), (P), (GFP), \ + __fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \ +}) + +#endif /* __AA_LIB_H */ diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h new file mode 100644 index 000000000..884489590 --- /dev/null +++ b/security/apparmor/include/match.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor policy dfa matching engine definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2012 Canonical Ltd. + */ + +#ifndef __AA_MATCH_H +#define __AA_MATCH_H + +#include <linux/kref.h> + +#define DFA_NOMATCH 0 +#define DFA_START 1 + + +/** + * The format used for transition tables is based on the GNU flex table + * file format (--tables-file option; see Table File Format in the flex + * info pages and the flex sources for documentation). The magic number + * used in the header is 0x1B5E783D instead of 0xF13C57B1 though, because + * new tables have been defined and others YY_ID_CHK (check) and YY_ID_DEF + * (default) tables are used slightly differently (see the apparmor-parser + * package). + * + * + * The data in the packed dfa is stored in network byte order, and the tables + * are arranged for flexibility. We convert the table data to host native + * byte order. + * + * The dfa begins with a table set header, and is followed by the actual + * tables. + */ + +#define YYTH_MAGIC 0x1B5E783D +#define YYTH_FLAG_DIFF_ENCODE 1 +#define YYTH_FLAG_OOB_TRANS 2 +#define YYTH_FLAGS (YYTH_FLAG_DIFF_ENCODE | YYTH_FLAG_OOB_TRANS) + +#define MAX_OOB_SUPPORTED 1 + +struct table_set_header { + u32 th_magic; /* YYTH_MAGIC */ + u32 th_hsize; + u32 th_ssize; + u16 th_flags; + char th_version[]; +}; + +/* The YYTD_ID are one less than flex table mappings. The flex id + * has 1 subtracted at table load time, this allows us to directly use the + * ID's as indexes. + */ +#define YYTD_ID_ACCEPT 0 +#define YYTD_ID_BASE 1 +#define YYTD_ID_CHK 2 +#define YYTD_ID_DEF 3 +#define YYTD_ID_EC 4 +#define YYTD_ID_META 5 +#define YYTD_ID_ACCEPT2 6 +#define YYTD_ID_NXT 7 +#define YYTD_ID_TSIZE 8 +#define YYTD_ID_MAX 8 + +#define YYTD_DATA8 1 +#define YYTD_DATA16 2 +#define YYTD_DATA32 4 +#define YYTD_DATA64 8 + +/* ACCEPT & ACCEPT2 tables gets 6 dedicated flags, YYTD_DATAX define the + * first flags + */ +#define ACCEPT1_FLAGS(X) ((X) & 0x3f) +#define ACCEPT2_FLAGS(X) ACCEPT1_FLAGS((X) >> YYTD_ID_ACCEPT2) +#define TO_ACCEPT1_FLAG(X) ACCEPT1_FLAGS(X) +#define TO_ACCEPT2_FLAG(X) (ACCEPT1_FLAGS(X) << YYTD_ID_ACCEPT2) +#define DFA_FLAG_VERIFY_STATES 0x1000 + +struct table_header { + u16 td_id; + u16 td_flags; + u32 td_hilen; + u32 td_lolen; + char td_data[]; +}; + +#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) +#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) +#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) +#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) +#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) +#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) +#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) + +struct aa_dfa { + struct kref count; + u16 flags; + u32 max_oob; + struct table_header *tables[YYTD_ID_TSIZE]; +}; + +extern struct aa_dfa *nulldfa; +extern struct aa_dfa *stacksplitdfa; + +#define byte_to_byte(X) (X) + +#define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \ + do { \ + typeof(LEN) __i; \ + TTYPE *__t = (TTYPE *) TABLE; \ + BTYPE *__b = (BTYPE *) BLOB; \ + for (__i = 0; __i < LEN; __i++) { \ + __t[__i] = NTOHX(__b[__i]); \ + } \ + } while (0) + +static inline size_t table_size(size_t len, size_t el_size) +{ + return ALIGN(sizeof(struct table_header) + len * el_size, 8); +} + +int aa_setup_dfa_engine(void); +void aa_teardown_dfa_engine(void); + +struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); +unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, + const char *str, int len); +unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, + const char *str); +unsigned int aa_dfa_next(struct aa_dfa *dfa, unsigned int state, + const char c); +unsigned int aa_dfa_outofband_transition(struct aa_dfa *dfa, + unsigned int state); +unsigned int aa_dfa_match_until(struct aa_dfa *dfa, unsigned int start, + const char *str, const char **retpos); +unsigned int aa_dfa_matchn_until(struct aa_dfa *dfa, unsigned int start, + const char *str, int n, const char **retpos); + +void aa_dfa_free_kref(struct kref *kref); + +#define WB_HISTORY_SIZE 24 +struct match_workbuf { + unsigned int count; + unsigned int pos; + unsigned int len; + unsigned int size; /* power of 2, same as history size */ + unsigned int history[WB_HISTORY_SIZE]; +}; +#define DEFINE_MATCH_WB(N) \ +struct match_workbuf N = { \ + .count = 0, \ + .pos = 0, \ + .len = 0, \ +} + +unsigned int aa_dfa_leftmatch(struct aa_dfa *dfa, unsigned int start, + const char *str, unsigned int *count); + +/** + * aa_get_dfa - increment refcount on dfa @p + * @dfa: dfa (MAYBE NULL) + * + * Returns: pointer to @dfa if @dfa is NULL will return NULL + * Requires: @dfa must be held with valid refcount when called + */ +static inline struct aa_dfa *aa_get_dfa(struct aa_dfa *dfa) +{ + if (dfa) + kref_get(&(dfa->count)); + + return dfa; +} + +/** + * aa_put_dfa - put a dfa refcount + * @dfa: dfa to put refcount (MAYBE NULL) + * + * Requires: if @dfa != NULL that a valid refcount be held + */ +static inline void aa_put_dfa(struct aa_dfa *dfa) +{ + if (dfa) + kref_put(&dfa->count, aa_dfa_free_kref); +} + +#define MATCH_FLAG_DIFF_ENCODE 0x80000000 +#define MARK_DIFF_ENCODE 0x40000000 +#define MATCH_FLAG_OOB_TRANSITION 0x20000000 +#define MATCH_FLAGS_MASK 0xff000000 +#define MATCH_FLAGS_VALID (MATCH_FLAG_DIFF_ENCODE | MATCH_FLAG_OOB_TRANSITION) +#define MATCH_FLAGS_INVALID (MATCH_FLAGS_MASK & ~MATCH_FLAGS_VALID) + +#endif /* __AA_MATCH_H */ diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h new file mode 100644 index 000000000..a710683b2 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright 2017 Canonical Ltd. + */ + +#ifndef __AA_MOUNT_H +#define __AA_MOUNT_H + +#include <linux/fs.h> +#include <linux/path.h> + +#include "domain.h" +#include "policy.h" + +/* mount perms */ +#define AA_MAY_PIVOTROOT 0x01 +#define AA_MAY_MOUNT 0x02 +#define AA_MAY_UMOUNT 0x04 +#define AA_AUDIT_DATA 0x40 +#define AA_MNT_CONT_MATCH 0x40 + +#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) + +int aa_remount(struct aa_label *label, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(struct aa_label *label, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(struct aa_label *label, const struct path *path, + unsigned long flags); + +int aa_move_mount(struct aa_label *label, const struct path *path, + const char *old_name); + +int aa_new_mount(struct aa_label *label, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); + +int aa_pivotroot(struct aa_label *label, const struct path *old_path, + const struct path *new_path); + +#endif /* __AA_MOUNT_H */ diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h new file mode 100644 index 000000000..aadb4b29f --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor network mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + */ + +#ifndef __AA_NET_H +#define __AA_NET_H + +#include <net/sock.h> +#include <linux/path.h> + +#include "apparmorfs.h" +#include "label.h" +#include "perms.h" +#include "policy.h" + +#define AA_MAY_SEND AA_MAY_WRITE +#define AA_MAY_RECEIVE AA_MAY_READ + +#define AA_MAY_SHUTDOWN AA_MAY_DELETE + +#define AA_MAY_CONNECT AA_MAY_OPEN +#define AA_MAY_ACCEPT 0x00100000 + +#define AA_MAY_BIND 0x00200000 +#define AA_MAY_LISTEN 0x00400000 + +#define AA_MAY_SETOPT 0x01000000 +#define AA_MAY_GETOPT 0x02000000 + +#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \ + AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \ + AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT) + +#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ + AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\ + AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \ + AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \ + AA_MAY_MPROT) + +#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ + AA_MAY_ACCEPT) +struct aa_sk_ctx { + struct aa_label *label; + struct aa_label *peer; +}; + +#define SK_CTX(X) ((X)->sk_security) +#define SOCK_ctx(X) SOCK_INODE(X)->i_security +#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ + struct lsm_network_audit NAME ## _net = { .sk = (SK), \ + .family = (F)}; \ + DEFINE_AUDIT_DATA(NAME, \ + ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ + LSM_AUDIT_DATA_NONE, \ + OP); \ + NAME.u.net = &(NAME ## _net); \ + aad(&NAME)->net.type = (T); \ + aad(&NAME)->net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, SK) \ + DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + + +#define af_select(FAMILY, FN, DEF_FN) \ +({ \ + int __e; \ + switch ((FAMILY)) { \ + default: \ + __e = DEF_FN; \ + } \ + __e; \ +}) + +struct aa_secmark { + u8 audit; + u8 deny; + u32 secid; + char *label; +}; + +extern struct aa_sfs_entry aa_sfs_entry_network[]; + +void audit_net_cb(struct audit_buffer *ab, void *va); +int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type); +int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, + int type, int protocol); +static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + struct common_audit_data *sa, + u32 request, + struct sock *sk) +{ + return aa_profile_af_perm(profile, sa, request, sk->sk_family, + sk->sk_type); +} +int aa_sk_perm(const char *op, u32 request, struct sock *sk); + +int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +int apparmor_secmark_check(struct aa_label *label, char *op, u32 request, + u32 secid, const struct sock *sk); + +#endif /* __AA_NET_H */ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h new file mode 100644 index 000000000..343189903 --- /dev/null +++ b/security/apparmor/include/path.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor basic path manipulation function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_PATH_H +#define __AA_PATH_H + +enum path_flags { + PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ + PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ + PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ + + PATH_DELEGATE_DELETED = 0x10000, /* delegate deleted files */ + PATH_MEDIATE_DELETED = 0x20000, /* mediate deleted paths */ +}; + +int aa_path_name(const struct path *path, int flags, char *buffer, + const char **name, const char **info, + const char *disconnected); + +#define IN_ATOMIC true +char *aa_get_buffer(bool in_atomic); +void aa_put_buffer(char *buf); + +#endif /* __AA_PATH_H */ diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h new file mode 100644 index 000000000..13f20c598 --- /dev/null +++ b/security/apparmor/include/perms.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor basic permission sets definitions. + * + * Copyright 2017 Canonical Ltd. + */ + +#ifndef __AA_PERM_H +#define __AA_PERM_H + +#include <linux/fs.h> +#include "label.h" + +#define AA_MAY_EXEC MAY_EXEC +#define AA_MAY_WRITE MAY_WRITE +#define AA_MAY_READ MAY_READ +#define AA_MAY_APPEND MAY_APPEND + +#define AA_MAY_CREATE 0x0010 +#define AA_MAY_DELETE 0x0020 +#define AA_MAY_OPEN 0x0040 +#define AA_MAY_RENAME 0x0080 /* pair */ + +#define AA_MAY_SETATTR 0x0100 /* meta write */ +#define AA_MAY_GETATTR 0x0200 /* meta read */ +#define AA_MAY_SETCRED 0x0400 /* security cred/attr */ +#define AA_MAY_GETCRED 0x0800 + +#define AA_MAY_CHMOD 0x1000 /* pair */ +#define AA_MAY_CHOWN 0x2000 /* pair */ +#define AA_MAY_CHGRP 0x4000 /* pair */ +#define AA_MAY_LOCK 0x8000 /* LINK_SUBSET overlaid */ + +#define AA_EXEC_MMAP 0x00010000 +#define AA_MAY_MPROT 0x00020000 /* extend conditions */ +#define AA_MAY_LINK 0x00040000 /* pair */ +#define AA_MAY_SNAPSHOT 0x00080000 /* pair */ + +#define AA_MAY_DELEGATE +#define AA_CONT_MATCH 0x08000000 + +#define AA_MAY_STACK 0x10000000 +#define AA_MAY_ONEXEC 0x20000000 /* either stack or change_profile */ +#define AA_MAY_CHANGE_PROFILE 0x40000000 +#define AA_MAY_CHANGEHAT 0x80000000 + +#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ + + +#define PERMS_CHRS_MASK (MAY_READ | MAY_WRITE | AA_MAY_CREATE | \ + AA_MAY_DELETE | AA_MAY_LINK | AA_MAY_LOCK | \ + AA_MAY_EXEC | AA_EXEC_MMAP | AA_MAY_APPEND) + +#define PERMS_NAMES_MASK (PERMS_CHRS_MASK | AA_MAY_OPEN | AA_MAY_RENAME | \ + AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_SETCRED | \ + AA_MAY_GETCRED | AA_MAY_CHMOD | AA_MAY_CHOWN | \ + AA_MAY_CHGRP | AA_MAY_MPROT | AA_MAY_SNAPSHOT | \ + AA_MAY_STACK | AA_MAY_ONEXEC | \ + AA_MAY_CHANGE_PROFILE | AA_MAY_CHANGEHAT) + +extern const char aa_file_perm_chrs[]; +extern const char *aa_file_perm_names[]; + +struct aa_perms { + u32 allow; + u32 audit; /* set only when allow is set */ + + u32 deny; /* explicit deny, or conflict if allow also set */ + u32 quiet; /* set only when ~allow | deny */ + u32 kill; /* set only when ~allow | deny */ + u32 stop; /* set only when ~allow | deny */ + + u32 complain; /* accumulates only used when ~allow & ~deny */ + u32 cond; /* set only when ~allow and ~deny */ + + u32 hide; /* set only when ~allow | deny */ + u32 prompt; /* accumulates only used when ~allow & ~deny */ + + /* Reserved: + * u32 subtree; / * set only when allow is set * / + */ + u16 xindex; +}; + +#define ALL_PERMS_MASK 0xffffffff +extern struct aa_perms nullperms; +extern struct aa_perms allperms; + + +#define xcheck(FN1, FN2) \ +({ \ + int e, error = FN1; \ + e = FN2; \ + if (e) \ + error = e; \ + error; \ +}) + + +/* + * TODO: update for labels pointing to labels instead of profiles + * TODO: optimize the walk, currently does subwalk of L2 for each P in L1 + * gah this doesn't allow for label compound check!!!! + */ +#define xcheck_ns_profile_profile(P1, P2, FN, args...) \ +({ \ + int ____e = 0; \ + if (P1->ns == P2->ns) \ + ____e = FN((P1), (P2), args); \ + (____e); \ +}) + +#define xcheck_ns_profile_label(P, L, FN, args...) \ +({ \ + struct aa_profile *__p2; \ + fn_for_each((L), __p2, \ + xcheck_ns_profile_profile((P), __p2, (FN), args)); \ +}) + +#define xcheck_ns_labels(L1, L2, FN, args...) \ +({ \ + struct aa_profile *__p1; \ + fn_for_each((L1), __p1, FN(__p1, (L2), args)); \ +}) + +/* Do the cross check but applying FN at the profiles level */ +#define xcheck_labels_profiles(L1, L2, FN, args...) \ + xcheck_ns_labels((L1), (L2), xcheck_ns_profile_label, (FN), args) + +#define xcheck_labels(L1, L2, P, FN1, FN2) \ + xcheck(fn_for_each((L1), (P), (FN1)), fn_for_each((L2), (P), (FN2))) + + +void aa_perm_mask_to_str(char *str, size_t str_size, const char *chrs, + u32 mask); +void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, + u32 mask); +void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, + u32 chrsmask, const char * const *names, u32 namesmask); +void aa_apply_modes_to_perms(struct aa_profile *profile, + struct aa_perms *perms); +void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, + struct aa_perms *perms); +void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend); +void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend); +void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label, + int type, u32 request, struct aa_perms *perms); +int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, + u32 request, int type, u32 *deny, + struct common_audit_data *sa); +int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms, + u32 request, struct common_audit_data *sa, + void (*cb)(struct audit_buffer *, void *)); +#endif /* __AA_PERM_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h new file mode 100644 index 000000000..639b5b248 --- /dev/null +++ b/security/apparmor/include/policy.h @@ -0,0 +1,315 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor policy definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_POLICY_H +#define __AA_POLICY_H + +#include <linux/capability.h> +#include <linux/cred.h> +#include <linux/kref.h> +#include <linux/rhashtable.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/socket.h> + +#include "apparmor.h" +#include "audit.h" +#include "capability.h" +#include "domain.h" +#include "file.h" +#include "lib.h" +#include "label.h" +#include "net.h" +#include "perms.h" +#include "resource.h" + + +struct aa_ns; + +extern int unprivileged_userns_apparmor_policy; + +extern const char *const aa_profile_mode_names[]; +#define APPARMOR_MODE_NAMES_MAX_INDEX 4 + +#define PROFILE_MODE(_profile, _mode) \ + ((aa_g_profile_mode == (_mode)) || \ + ((_profile)->mode == (_mode))) + +#define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN) + +#define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL) + +#define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT) + +#define CHECK_DEBUG1(_profile) ((_profile)->label.flags & FLAG_DEBUG1) + +#define CHECK_DEBUG2(_profile) ((_profile)->label.flags & FLAG_DEBUG2) + +#define profile_is_stale(_profile) (label_is_stale(&(_profile)->label)) + +#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) + +/* + * FIXME: currently need a clean way to replace and remove profiles as a + * set. It should be done at the namespace level. + * Either, with a set of profiles loaded at the namespace level or via + * a mark and remove marked interface. + */ +enum profile_mode { + APPARMOR_ENFORCE, /* enforce access rules */ + APPARMOR_COMPLAIN, /* allow and log access violations */ + APPARMOR_KILL, /* kill task on access violation */ + APPARMOR_UNCONFINED, /* profile set to unconfined */ +}; + + +/* struct aa_policydb - match engine for a policy + * dfa: dfa pattern match + * start: set of start states for the different classes of data + */ +struct aa_policydb { + /* Generic policy DFA specific rule types will be subsections of it */ + struct aa_dfa *dfa; + unsigned int start[AA_CLASS_LAST + 1]; + +}; + +/* struct aa_data - generic data structure + * key: name for retrieving this data + * size: size of data in bytes + * data: binary data + * head: reserved for rhashtable + */ +struct aa_data { + char *key; + u32 size; + char *data; + struct rhash_head head; +}; + + +/* struct aa_profile - basic confinement data + * @base - base components of the profile (name, refcount, lists, lock ...) + * @label - label this profile is an extension of + * @parent: parent of profile + * @ns: namespace the profile is in + * @rename: optional profile name that this profile renamed + * @attach: human readable attachment string + * @xmatch: optional extended matching for unconfined executables names + * @xmatch_len: xmatch prefix len, used to determine xmatch priority + * @audit: the auditing mode of the profile + * @mode: the enforcement mode of the profile + * @path_flags: flags controlling path generation behavior + * @disconnected: what to prepend if attach_disconnected is specified + * @size: the memory consumed by this profiles rules + * @policy: general match rules governing policy + * @file: The set of rules governing basic file access and domain transitions + * @caps: capabilities for the profile + * @rlimits: rlimits for the profile + * + * @dents: dentries for the profiles file entries in apparmorfs + * @dirname: name of the profile dir in apparmorfs + * @data: hashtable for free-form policy aa_data + * + * The AppArmor profile contains the basic confinement data. Each profile + * has a name, and exists in a namespace. The @name and @exec_match are + * used to determine profile attachment against unconfined tasks. All other + * attachments are determined by profile X transition rules. + * + * Profiles have a hierarchy where hats and children profiles keep + * a reference to their parent. + * + * Profile names can not begin with a : and can not contain the \0 + * character. If a profile name begins with / it will be considered when + * determining profile attachment on "unconfined" tasks. + */ +struct aa_profile { + struct aa_policy base; + struct aa_profile __rcu *parent; + + struct aa_ns *ns; + const char *rename; + + const char *attach; + struct aa_dfa *xmatch; + unsigned int xmatch_len; + enum audit_mode audit; + long mode; + u32 path_flags; + const char *disconnected; + int size; + + struct aa_policydb policy; + struct aa_file_rules file; + struct aa_caps caps; + + int xattr_count; + char **xattrs; + + struct aa_rlimit rlimits; + + int secmark_count; + struct aa_secmark *secmark; + + struct aa_loaddata *rawdata; + unsigned char *hash; + char *dirname; + struct dentry *dents[AAFS_PROF_SIZEOF]; + struct rhashtable *data; + struct aa_label label; +}; + +extern enum profile_mode aa_g_profile_mode; + +#define AA_MAY_LOAD_POLICY AA_MAY_APPEND +#define AA_MAY_REPLACE_POLICY AA_MAY_WRITE +#define AA_MAY_REMOVE_POLICY AA_MAY_DELETE + +#define profiles_ns(P) ((P)->ns) +#define name_is_shared(A, B) ((A)->hname && (A)->hname == (B)->hname) + +void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); + + +void aa_free_proxy_kref(struct kref *kref); +struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy, + gfp_t gfp); +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp); +void aa_free_profile(struct aa_profile *profile); +void aa_free_profile_kref(struct kref *kref); +struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); +struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, + size_t n); +struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); +struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, + const char *fqname, size_t n); +struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); + +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label, + u32 mask, struct aa_loaddata *udata); +ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label, + char *name, size_t size); +void __aa_profile_list_release(struct list_head *head); + +#define PROF_ADD 1 +#define PROF_REPLACE 0 + +#define profile_unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) + +/** + * aa_get_newest_profile - simple wrapper fn to wrap the label version + * @p: profile (NOT NULL) + * + * Returns refcount to newest version of the profile (maybe @p) + * + * Requires: @p must be held with a valid refcount + */ +static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) +{ + return labels_profile(aa_get_newest_label(&p->label)); +} + +static inline unsigned int PROFILE_MEDIATES(struct aa_profile *profile, + unsigned char class) +{ + if (class <= AA_CLASS_LAST) + return profile->policy.start[class]; + else + return aa_dfa_match_len(profile->policy.dfa, + profile->policy.start[0], &class, 1); +} + +static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + u16 AF) { + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); + __be16 be_af = cpu_to_be16(AF); + + if (!state) + return 0; + return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); +} + +/** + * aa_get_profile - increment refcount on profile @p + * @p: profile (MAYBE NULL) + * + * Returns: pointer to @p if @p is NULL will return NULL + * Requires: @p must be held with valid refcount when called + */ +static inline struct aa_profile *aa_get_profile(struct aa_profile *p) +{ + if (p) + kref_get(&(p->label.count)); + + return p; +} + +/** + * aa_get_profile_not0 - increment refcount on profile @p found via lookup + * @p: profile (MAYBE NULL) + * + * Returns: pointer to @p if @p is NULL will return NULL + * Requires: @p must be held with valid refcount when called + */ +static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) +{ + if (p && kref_get_unless_zero(&p->label.count)) + return p; + + return NULL; +} + +/** + * aa_get_profile_rcu - increment a refcount profile that can be replaced + * @p: pointer to profile that can be replaced (NOT NULL) + * + * Returns: pointer to a refcounted profile. + * else NULL if no profile + */ +static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) +{ + struct aa_profile *c; + + rcu_read_lock(); + do { + c = rcu_dereference(*p); + } while (c && !kref_get_unless_zero(&c->label.count)); + rcu_read_unlock(); + + return c; +} + +/** + * aa_put_profile - decrement refcount on profile @p + * @p: profile (MAYBE NULL) + */ +static inline void aa_put_profile(struct aa_profile *p) +{ + if (p) + kref_put(&p->label.count, aa_label_kref); +} + +static inline int AUDIT_MODE(struct aa_profile *profile) +{ + if (aa_g_audit != AUDIT_NORMAL) + return aa_g_audit; + + return profile->audit; +} + +bool aa_policy_view_capable(struct aa_label *label, struct aa_ns *ns); +bool aa_policy_admin_capable(struct aa_label *label, struct aa_ns *ns); +int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, + u32 mask); +bool aa_current_policy_view_capable(struct aa_ns *ns); +bool aa_current_policy_admin_capable(struct aa_ns *ns); + +#endif /* __AA_POLICY_H */ diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h new file mode 100644 index 000000000..33d665516 --- /dev/null +++ b/security/apparmor/include/policy_ns.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor policy definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2017 Canonical Ltd. + */ + +#ifndef __AA_NAMESPACE_H +#define __AA_NAMESPACE_H + +#include <linux/kref.h> + +#include "apparmor.h" +#include "apparmorfs.h" +#include "label.h" +#include "policy.h" + + +/* struct aa_ns_acct - accounting of profiles in namespace + * @max_size: maximum space allowed for all profiles in namespace + * @max_count: maximum number of profiles that can be in this namespace + * @size: current size of profiles + * @count: current count of profiles (includes null profiles) + */ +struct aa_ns_acct { + int max_size; + int max_count; + int size; + int count; +}; + +/* struct aa_ns - namespace for a set of profiles + * @base: common policy + * @parent: parent of namespace + * @lock: lock for modifying the object + * @acct: accounting for the namespace + * @unconfined: special unconfined profile for the namespace + * @sub_ns: list of namespaces under the current namespace. + * @uniq_null: uniq value used for null learning profiles + * @uniq_id: a unique id count for the profiles in the namespace + * @level: level of ns within the tree hierarchy + * @dents: dentries for the namespaces file entries in apparmorfs + * + * An aa_ns defines the set profiles that are searched to determine which + * profile to attach to a task. Profiles can not be shared between aa_ns + * and profile names within a namespace are guaranteed to be unique. When + * profiles in separate namespaces have the same name they are NOT considered + * to be equivalent. + * + * Namespaces are hierarchical and only namespaces and profiles below the + * current namespace are visible. + * + * Namespace names must be unique and can not contain the characters :/\0 + */ +struct aa_ns { + struct aa_policy base; + struct aa_ns *parent; + struct mutex lock; + struct aa_ns_acct acct; + struct aa_profile *unconfined; + struct list_head sub_ns; + atomic_t uniq_null; + long uniq_id; + int level; + long revision; + wait_queue_head_t wait; + + struct aa_labelset labels; + struct list_head rawdata_list; + + struct dentry *dents[AAFS_NS_SIZEOF]; +}; + +extern struct aa_label *kernel_t; +extern struct aa_ns *root_ns; + +extern const char *aa_hidden_ns_name; + +#define ns_unconfined(NS) (&(NS)->unconfined->label) + +bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns); +const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns); +void aa_free_ns(struct aa_ns *ns); +int aa_alloc_root_ns(void); +void aa_free_root_ns(void); +void aa_free_ns_kref(struct kref *kref); + +struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); +struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); +struct aa_ns *__aa_lookupn_ns(struct aa_ns *view, const char *hname, size_t n); +struct aa_ns *aa_lookupn_ns(struct aa_ns *view, const char *name, size_t n); +struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, + struct dentry *dir); +struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name); +void __aa_remove_ns(struct aa_ns *ns); + +static inline struct aa_profile *aa_deref_parent(struct aa_profile *p) +{ + return rcu_dereference_protected(p->parent, + mutex_is_locked(&p->ns->lock)); +} + +/** + * aa_get_ns - increment references count on @ns + * @ns: namespace to increment reference count of (MAYBE NULL) + * + * Returns: pointer to @ns, if @ns is NULL returns NULL + * Requires: @ns must be held with valid refcount when called + */ +static inline struct aa_ns *aa_get_ns(struct aa_ns *ns) +{ + if (ns) + aa_get_profile(ns->unconfined); + + return ns; +} + +/** + * aa_put_ns - decrement refcount on @ns + * @ns: namespace to put reference of + * + * Decrement reference count of @ns and if no longer in use free it + */ +static inline void aa_put_ns(struct aa_ns *ns) +{ + if (ns) + aa_put_profile(ns->unconfined); +} + +/** + * __aa_findn_ns - find a namespace on a list by @name + * @head: list to search for namespace on (NOT NULL) + * @name: name of namespace to look for (NOT NULL) + * @n: length of @name + * Returns: unrefcounted namespace + * + * Requires: rcu_read_lock be held + */ +static inline struct aa_ns *__aa_findn_ns(struct list_head *head, + const char *name, size_t n) +{ + return (struct aa_ns *)__policy_strn_find(head, name, n); +} + +static inline struct aa_ns *__aa_find_ns(struct list_head *head, + const char *name) +{ + return __aa_findn_ns(head, name, strlen(name)); +} + +static inline struct aa_ns *__aa_lookup_ns(struct aa_ns *base, + const char *hname) +{ + return __aa_lookupn_ns(base, hname, strlen(hname)); +} + +static inline struct aa_ns *aa_lookup_ns(struct aa_ns *view, const char *name) +{ + return aa_lookupn_ns(view, name, strlen(name)); +} + +#endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h new file mode 100644 index 000000000..e89b70144 --- /dev/null +++ b/security/apparmor/include/policy_unpack.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor policy loading interface function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __POLICY_INTERFACE_H +#define __POLICY_INTERFACE_H + +#include <linux/list.h> +#include <linux/kref.h> +#include <linux/dcache.h> +#include <linux/workqueue.h> + +struct aa_load_ent { + struct list_head list; + struct aa_profile *new; + struct aa_profile *old; + struct aa_profile *rename; + const char *ns_name; +}; + +void aa_load_ent_free(struct aa_load_ent *ent); +struct aa_load_ent *aa_load_ent_alloc(void); + +#define PACKED_FLAG_HAT 1 +#define PACKED_FLAG_DEBUG1 2 +#define PACKED_FLAG_DEBUG2 4 + +#define PACKED_MODE_ENFORCE 0 +#define PACKED_MODE_COMPLAIN 1 +#define PACKED_MODE_KILL 2 +#define PACKED_MODE_UNCONFINED 3 + +struct aa_ns; + +enum { + AAFS_LOADDATA_ABI = 0, + AAFS_LOADDATA_REVISION, + AAFS_LOADDATA_HASH, + AAFS_LOADDATA_DATA, + AAFS_LOADDATA_COMPRESSED_SIZE, + AAFS_LOADDATA_DIR, /* must be last actual entry */ + AAFS_LOADDATA_NDENTS /* count of entries */ +}; + +/* + * The AppArmor interface treats data as a type byte followed by the + * actual data. The interface has the notion of a named entry + * which has a name (AA_NAME typecode followed by name string) followed by + * the entries typecode and data. Named types allow for optional + * elements and extensions to be added and tested for without breaking + * backwards compatibility. + */ + +enum aa_code { + AA_U8, + AA_U16, + AA_U32, + AA_U64, + AA_NAME, /* same as string except it is items name */ + AA_STRING, + AA_BLOB, + AA_STRUCT, + AA_STRUCTEND, + AA_LIST, + AA_LISTEND, + AA_ARRAY, + AA_ARRAYEND, +}; + +/* + * aa_ext is the read of the buffer containing the serialized profile. The + * data is copied into a kernel buffer in apparmorfs and then handed off to + * the unpack routines. + */ +struct aa_ext { + void *start; + void *end; + void *pos; /* pointer to current position in the buffer */ + u32 version; +}; + +/* + * struct aa_loaddata - buffer of policy raw_data set + * + * there is no loaddata ref for being on ns list, nor a ref from + * d_inode(@dentry) when grab a ref from these, @ns->lock must be held + * && __aa_get_loaddata() needs to be used, and the return value + * checked, if NULL the loaddata is already being reaped and should be + * considered dead. + */ +struct aa_loaddata { + struct kref count; + struct list_head list; + struct work_struct work; + struct dentry *dents[AAFS_LOADDATA_NDENTS]; + struct aa_ns *ns; + char *name; + size_t size; /* the original size of the payload */ + size_t compressed_size; /* the compressed size of the payload */ + long revision; /* the ns policy revision this caused */ + int abi; + unsigned char *hash; + + /* Pointer to payload. If @compressed_size > 0, then this is the + * compressed version of the payload, else it is the uncompressed + * version (with the size indicated by @size). + */ + char *data; +}; + +int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); + +/** + * __aa_get_loaddata - get a reference count to uncounted data reference + * @data: reference to get a count on + * + * Returns: pointer to reference OR NULL if race is lost and reference is + * being repeated. + * Requires: @data->ns->lock held, and the return code MUST be checked + * + * Use only from inode->i_private and @data->list found references + */ +static inline struct aa_loaddata * +__aa_get_loaddata(struct aa_loaddata *data) +{ + if (data && kref_get_unless_zero(&(data->count))) + return data; + + return NULL; +} + +/** + * aa_get_loaddata - get a reference count from a counted data reference + * @data: reference to get a count on + * + * Returns: point to reference + * Requires: @data to have a valid reference count on it. It is a bug + * if the race to reap can be encountered when it is used. + */ +static inline struct aa_loaddata * +aa_get_loaddata(struct aa_loaddata *data) +{ + struct aa_loaddata *tmp = __aa_get_loaddata(data); + + AA_BUG(data && !tmp); + + return tmp; +} + +void __aa_loaddata_update(struct aa_loaddata *data, long revision); +bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); +void aa_loaddata_kref(struct kref *kref); +struct aa_loaddata *aa_loaddata_alloc(size_t size); +static inline void aa_put_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_put(&data->count, aa_loaddata_kref); +} + +#if IS_ENABLED(CONFIG_KUNIT) +bool aa_inbounds(struct aa_ext *e, size_t size); +size_t aa_unpack_u16_chunk(struct aa_ext *e, char **chunk); +bool aa_unpack_X(struct aa_ext *e, enum aa_code code); +bool aa_unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name); +bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name); +bool aa_unpack_u64(struct aa_ext *e, u64 *data, const char *name); +size_t aa_unpack_array(struct aa_ext *e, const char *name); +size_t aa_unpack_blob(struct aa_ext *e, char **blob, const char *name); +int aa_unpack_str(struct aa_ext *e, const char **string, const char *name); +int aa_unpack_strdup(struct aa_ext *e, char **string, const char *name); +#endif + +#endif /* __POLICY_INTERFACE_H */ diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h new file mode 100644 index 000000000..31689437e --- /dev/null +++ b/security/apparmor/include/procattr.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor /proc/<pid>/attr/ interface function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_PROCATTR_H +#define __AA_PROCATTR_H + +int aa_getprocattr(struct aa_label *label, char **string); +int aa_setprocattr_changehat(char *args, size_t size, int flags); + +#endif /* __AA_PROCATTR_H */ diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h new file mode 100644 index 000000000..961d85d32 --- /dev/null +++ b/security/apparmor/include/resource.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor resource limits function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + */ + +#ifndef __AA_RESOURCE_H +#define __AA_RESOURCE_H + +#include <linux/resource.h> +#include <linux/sched.h> + +#include "apparmorfs.h" + +struct aa_profile; + +/* struct aa_rlimit - rlimit settings for the profile + * @mask: which hard limits to set + * @limits: rlimit values that override task limits + * + * AppArmor rlimits are used to set confined task rlimits. Only the + * limits specified in @mask will be controlled by apparmor. + */ +struct aa_rlimit { + unsigned int mask; + struct rlimit limits[RLIM_NLIMITS]; +}; + +extern struct aa_sfs_entry aa_sfs_entry_rlimit[]; + +int aa_map_resource(int resource); +int aa_task_setrlimit(struct aa_label *label, struct task_struct *task, + unsigned int resource, struct rlimit *new_rlim); + +void __aa_transition_rlimits(struct aa_label *old, struct aa_label *new); + +static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) +{ + /* NOP */ +} + +#endif /* __AA_RESOURCE_H */ diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h new file mode 100644 index 000000000..a912a5d5d --- /dev/null +++ b/security/apparmor/include/secid.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (secid) definitions + * + * Copyright 2009-2018 Canonical Ltd. + */ + +#ifndef __AA_SECID_H +#define __AA_SECID_H + +#include <linux/slab.h> +#include <linux/types.h> + +struct aa_label; + +/* secid value that will not be allocated */ +#define AA_SECID_INVALID 0 + +/* secid value that matches any other secid */ +#define AA_SECID_WILDCARD 1 + +/* sysctl to enable displaying mode when converting secid to secctx */ +extern int apparmor_display_secid_mode; + +struct aa_label *aa_secid_to_label(u32 secid); +int apparmor_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); +int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); +void apparmor_release_secctx(char *secdata, u32 seclen); + + +int aa_alloc_secid(struct aa_label *label, gfp_t gfp); +void aa_free_secid(u32 secid); +void aa_secid_update(u32 secid, struct aa_label *label); + +#endif /* __AA_SECID_H */ diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h new file mode 100644 index 000000000..cbf7a997e --- /dev/null +++ b/security/apparmor/include/sig_names.h @@ -0,0 +1,101 @@ +#include <linux/signal.h> + +#define SIGUNKNOWN 0 +#define MAXMAPPED_SIG 35 +#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1) +#define SIGRT_BASE 128 + +/* provide a mapping of arch signal to internal signal # for mediation + * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO + * map to the same entry those that may/or may not get a separate entry + */ +static const int sig_map[MAXMAPPED_SIG] = { + [0] = MAXMAPPED_SIG, /* existence test */ + [SIGHUP] = 1, + [SIGINT] = 2, + [SIGQUIT] = 3, + [SIGILL] = 4, + [SIGTRAP] = 5, /* -, 5, - */ + [SIGABRT] = 6, /* SIGIOT: -, 6, - */ + [SIGBUS] = 7, /* 10, 7, 10 */ + [SIGFPE] = 8, + [SIGKILL] = 9, + [SIGUSR1] = 10, /* 30, 10, 16 */ + [SIGSEGV] = 11, + [SIGUSR2] = 12, /* 31, 12, 17 */ + [SIGPIPE] = 13, + [SIGALRM] = 14, + [SIGTERM] = 15, +#ifdef SIGSTKFLT + [SIGSTKFLT] = 16, /* -, 16, - */ +#endif + [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */ + [SIGCONT] = 18, /* 19, 18, 25 */ + [SIGSTOP] = 19, /* 17, 19, 23 */ + [SIGTSTP] = 20, /* 18, 20, 24 */ + [SIGTTIN] = 21, /* 21, 21, 26 */ + [SIGTTOU] = 22, /* 22, 22, 27 */ + [SIGURG] = 23, /* 16, 23, 21 */ + [SIGXCPU] = 24, /* 24, 24, 30 */ + [SIGXFSZ] = 25, /* 25, 25, 31 */ + [SIGVTALRM] = 26, /* 26, 26, 28 */ + [SIGPROF] = 27, /* 27, 27, 29 */ + [SIGWINCH] = 28, /* 28, 28, 20 */ + [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */ + [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */ +#ifdef SIGSYS + [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */ +#endif +#ifdef SIGEMT + [SIGEMT] = 32, /* 7, - , 7 */ +#endif +#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */ + [SIGLOST] = 33, /* unused on Linux */ +#endif +#if defined(SIGUNUSED) && \ + defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS + [SIGUNUSED] = 34, /* -, 31, - */ +#endif +}; + +/* this table is ordered post sig_map[sig] mapping */ +static const char *const sig_names[MAXMAPPED_SIGNAME] = { + "unknown", + "hup", + "int", + "quit", + "ill", + "trap", + "abrt", + "bus", + "fpe", + "kill", + "usr1", + "segv", + "usr2", + "pipe", + "alrm", + "term", + "stkflt", + "chld", + "cont", + "stop", + "stp", + "ttin", + "ttou", + "urg", + "xcpu", + "xfsz", + "vtalrm", + "prof", + "winch", + "io", + "pwr", + "sys", + "emt", + "lost", + "unused", + + "exists", /* always last existence test mapped to MAXMAPPED_SIG */ +}; + diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h new file mode 100644 index 000000000..13437d62c --- /dev/null +++ b/security/apparmor/include/task.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor task related definitions and mediation + * + * Copyright 2017 Canonical Ltd. + */ + +#ifndef __AA_TASK_H +#define __AA_TASK_H + +static inline struct aa_task_ctx *task_ctx(struct task_struct *task) +{ + return task->security + apparmor_blob_sizes.lbs_task; +} + +/* + * struct aa_task_ctx - information for current task label change + * @nnp: snapshot of label at time of no_new_privs + * @onexec: profile to transition to on next exec (MAY BE NULL) + * @previous: profile the task may return to (MAY BE NULL) + * @token: magic value the task must know for returning to @previous_profile + */ +struct aa_task_ctx { + struct aa_label *nnp; + struct aa_label *onexec; + struct aa_label *previous; + u64 token; +}; + +int aa_replace_current_label(struct aa_label *label); +int aa_set_current_onexec(struct aa_label *label, bool stack); +int aa_set_current_hat(struct aa_label *label, u64 token); +int aa_restore_previous_label(u64 cookie); +struct aa_label *aa_get_task_label(struct task_struct *task); + +/** + * aa_free_task_ctx - free a task_ctx + * @ctx: task_ctx to free (MAYBE NULL) + */ +static inline void aa_free_task_ctx(struct aa_task_ctx *ctx) +{ + if (ctx) { + aa_put_label(ctx->nnp); + aa_put_label(ctx->previous); + aa_put_label(ctx->onexec); + } +} + +/** + * aa_dup_task_ctx - duplicate a task context, incrementing reference counts + * @new: a blank task context (NOT NULL) + * @old: the task context to copy (NOT NULL) + */ +static inline void aa_dup_task_ctx(struct aa_task_ctx *new, + const struct aa_task_ctx *old) +{ + *new = *old; + aa_get_label(new->nnp); + aa_get_label(new->previous); + aa_get_label(new->onexec); +} + +/** + * aa_clear_task_ctx_trans - clear transition tracking info from the ctx + * @ctx: task context to clear (NOT NULL) + */ +static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) +{ + AA_BUG(!ctx); + + aa_put_label(ctx->previous); + aa_put_label(ctx->onexec); + ctx->previous = NULL; + ctx->onexec = NULL; + ctx->token = 0; +} + +#define AA_PTRACE_TRACE MAY_WRITE +#define AA_PTRACE_READ MAY_READ +#define AA_MAY_BE_TRACED AA_MAY_APPEND +#define AA_MAY_BE_READ AA_MAY_CREATE +#define PTRACE_PERM_SHIFT 2 + +#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ + AA_MAY_BE_READ | AA_MAY_BE_TRACED) +#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) + +#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ + "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ + "xcpu xfsz vtalrm prof winch io pwr sys emt lost" + +int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request); + + +#endif /* __AA_TASK_H */ |