diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-02 12:17:24 -0700 |
| commit | 3f1fe48a36b0b6722dc3fd421d93512bac138e9a (patch) | |
| tree | b767d7f6e26bc64334d3f14f8422d187239dc45d /security/apparmor/include | |
| download | linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.tar.gz linux-stable-3f1fe48a36b0b6722dc3fd421d93512bac138e9a.zip | |
Merge tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linuxgrafted
Pull io_uring fixes from Jens Axboe:
- Fix a task_work add use-after-free with SQPOLL.
The sqpoll thread could pop and complete the last request while
io_req_normal_work_add() was still looking at them after the mpscq
push.
Use the same approach as DEFER_TASKRUN to protect from that, holding
an RCU read lock across the add, and have exit wait for an RCU grace
period for SQPOLL rings as well.
- CQE32 ring fixes: correct the free entry check for 32b CQEs, zero the
big_cqe for aux CQEs, and only post the dummy skip CQE on CQE_MIXED
rings
- Mark the source filter table as COW when cloning bpf filters, so
registering another filter on the source doesn't modify the shared
table in place
- Initialize the task context before running the BPF loop
- Requeue zcrx multishot receives stopped by a local resource
- End a TX_TIMESTAMP multishot cmd when the CQ is full (lollipopkit)
* tag 'io_uring-7.3-20261002' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
io_uring: fix task_work add use-after-free with SQPOLL
io_uring/cmd_net: end TX_TIMESTAMP multishot when the CQ is full
io_uring/zcrx: requeue multishot receives stopped by a local resource
io_uring: initialize task context before running the BPF loop
io_uring: zero big_cqe for aux CQEs on CQE32 rings
io_uring: fix free entry check for 32b CQEs on CQE32 rings
io_uring: only post the dummy skip CQE on CQE_MIXED rings
io_uring/bpf_filter: mark source as COW when cloning filters
Diffstat (limited to 'security/apparmor/include')
28 files changed, 3733 insertions, 0 deletions
diff --git a/security/apparmor/include/af_inet.h b/security/apparmor/include/af_inet.h new file mode 100644 index 000000000..601ab44bd --- /dev/null +++ b/security/apparmor/include/af_inet.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor inet/inet6 fine grained mediation + * + * Copyright 2024 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ +#ifndef __AA_AF_INET_H +#define __AA_AF_INET_H + +#include "label.h" + +int aa_inet_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_inet_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_inet_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_inet_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_inet_listen_perm(struct socket *sock, int backlog); +int aa_inet_accept_perm(struct socket *sock, struct socket *newsock); +int aa_inet_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_inet_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_inet_file_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +#endif /* __AA_AF_INET_H */ diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h new file mode 100644 index 000000000..4a62e600d --- /dev/null +++ b/security/apparmor/include/af_unix.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor af_unix fine grained mediation + * + * Copyright 2023 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ +#ifndef __AA_AF_UNIX_H + +#include <net/af_unix.h> + +#include "label.h" + +#define unix_addr(A) ((struct sockaddr_un *)(A)) +#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) +#define unix_peer(sk) (unix_sk(sk)->peer) +#define is_unix_addr_abstract_name(B) ((B)[0] == 0) +#define is_unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) +#define is_unix_addr_fs(A, L) (!is_unix_addr_anon(A, L) && \ + !is_unix_addr_abstract_name(unix_addr(A)->sun_path)) + +#define is_unix_anonymous(U) (!unix_sk(U)->addr) +#define is_unix_fs(U) (!is_unix_anonymous(U) && \ + unix_sk(U)->addr->name->sun_path[0]) +#define is_unix_connected(S) ((S)->state == SS_CONNECTED) + + +struct sockaddr_un *aa_sunaddr(const struct unix_sock *u, int *addrlen); +int aa_unix_peer_perm(const struct cred *subj_cred, + struct aa_label *label, const char *op, u32 request, + struct sock *sk, struct sock *peer_sk, + struct aa_label *peer_label); +int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); +int aa_unix_create_perm(struct aa_label *label, int family, int type, + int protocol); +int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, + int addrlen); +int aa_unix_listen_perm(struct socket *sock, int backlog); +int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); +int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, + struct msghdr *msg, int size); +int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, + int optname); +int aa_unix_file_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, struct file *file); + +#endif /* __AA_AF_UNIX_H */ diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h new file mode 100644 index 000000000..cb06b2f55 --- /dev/null +++ b/security/apparmor/include/apparmor.h @@ -0,0 +1,69 @@ +/* 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_NONE 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_XMATCH 11 +#define AA_CLASS_NET 14 +#define AA_CLASS_NETV9 15 +#define AA_CLASS_LABEL 16 +#define AA_CLASS_POSIX_MQUEUE 17 +#define AA_CLASS_MODULE 19 +#define AA_CLASS_DISPLAY_LSM 20 +#define AA_CLASS_NS 21 +#define AA_CLASS_IO_URING 22 + +#define AA_CLASS_NETV9_SKB 30 +#define AA_CLASS_X 31 +#define AA_CLASS_DBUS 32 + +/* NOTE: if AA_CLASS_LAST > 63 need to update label->mediates */ +#define AA_CLASS_LAST AA_CLASS_DBUS + +/* Control parameters settable through module/boot flags */ +extern enum audit_mode aa_g_audit; +extern bool aa_g_audit_header; +extern int 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; + +#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY +#define AA_MIN_CLEVEL zstd_min_clevel() +#define AA_MAX_CLEVEL zstd_max_clevel() +#define AA_DEFAULT_CLEVEL ZSTD_CLEVEL_DEFAULT +#else +#define AA_MIN_CLEVEL 0 +#define AA_MAX_CLEVEL 0 +#define AA_DEFAULT_CLEVEL 0 +#endif /* CONFIG_SECURITY_APPARMOR_EXPORT_BINARY */ + + +#endif /* __APPARMOR_H */ diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h new file mode 100644 index 000000000..c6c8fcde7 --- /dev/null +++ b/security/apparmor/include/apparmorfs.h @@ -0,0 +1,151 @@ +/* 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 + +#include <linux/init.h> +#include <linux/types.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]) + +int aa_create_aafs(void); + +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); +void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile); +int __aa_create_rawdata_symlink_dents(struct aa_profile *profile); +#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; +} + +static inline void __aa_remove_rawdata_symlink_dents(struct aa_profile *profile) +{ + /* empty stub */ +} + +static inline int __aa_create_rawdata_symlink_dents(struct aa_profile *profile) +{ + 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..987ed4441 --- /dev/null +++ b/security/apparmor/include/audit.h @@ -0,0 +1,218 @@ +/* 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[]; +enum audit_mode { + AUDIT_NORMAL, /* follow normal auditing of accesses */ + AUDIT_QUIET_DENIED, /* quiet all denied access messages */ + AUDIT_QUIET_ALLOWED, /* quiet all allowed access messages */ + AUDIT_QUIET, /* quiet all messages */ + AUDIT_NOQUIET, /* do not quiet audit messages */ + AUDIT_ALL, /* audit all accesses */ + AUDIT_MODE_NAMES_COUNT /* Must be last entry */ +}; + +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" + +#define OP_USERNS_CREATE "userns_create" + +#define OP_URING_OVERRIDE "uring_override" +#define OP_URING_SQPOLL "uring_sqpoll" + +struct apparmor_audit_data { + int error; + int type; + u16 class; + const char *op; + const struct cred *subj_cred; + struct aa_label *subj_label; + const char *name; + const char *info; + u32 request; + u32 denied; + u32 tags; + + 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; + void *addr; + int addrlen; + struct { + void *addr; + int addrlen; + } peer; + } 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; + struct { + struct aa_label *target; + } uring; + }; + + struct common_audit_data common; +}; + +/* macros for dealing with apparmor_audit_data structure */ +#define aad(SA) (container_of(SA, struct apparmor_audit_data, common)) +#define aad_of_va(VA) aad((struct common_audit_data *)(VA)) + +#define DEFINE_AUDIT_DATA(NAME, T, C, X) \ + /* TODO: cleanup audit init so we don't need _aad = {0,} */ \ + struct apparmor_audit_data NAME = { \ + .class = (C), \ + .op = (X), \ + .common.type = (T), \ + .common.u.tsk = NULL, \ + .common.apparmor_audit_data = &NAME, \ + }; + +int aa_select_audit_type(u32 denied, const struct aa_perms *perms); + +void aa_audit_msg(int type, struct apparmor_audit_data *ad, + void (*cb) (struct audit_buffer *, void *)); +int aa_audit(int type, struct aa_profile *profile, + struct apparmor_audit_data *ad, + void (*cb) (struct audit_buffer *, void *)); + +#define aa_audit_error(ERROR, AD, CB) \ +({ \ + (AD)->error = (ERROR); \ + aa_audit_msg(AUDIT_APPARMOR_ERROR, (AD), (CB)); \ + (AD)->error; \ +}) + +int aa_audit_perm_error(struct aa_label *label, u32 request, int error, + struct apparmor_audit_data *ad, + void (*cb)(struct audit_buffer *, void *)); + +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, gfp_t gfp); +int aa_audit_rule_known(struct audit_krule *rule); +int aa_audit_rule_match(struct lsm_prop *prop, 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..89a9c75d8 --- /dev/null +++ b/security/apparmor/include/capability.h @@ -0,0 +1,49 @@ +/* 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/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[]; + +kernel_cap_t aa_profile_capget(const struct aa_profile *profile); +int aa_capable(const struct cred *subj_cred, 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..056e031b7 --- /dev/null +++ b/security/apparmor/include/cred.h @@ -0,0 +1,246 @@ +/* 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_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(cred_label(cred)); +} + +static inline struct aa_label *aa_get_newest_cred_label_condref(const struct cred *cred, + bool *needput) +{ + struct aa_label *l = cred_label(cred); + + if (unlikely(label_is_stale(l))) { + *needput = true; + return aa_get_newest_label(l); + } + + *needput = false; + return l; +} + +static inline void aa_put_label_condref(struct aa_label *l, bool needput) +{ + if (unlikely(needput)) + aa_put_label(l); +} + +/** + * 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 cred_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); +} + +/** + * __end_cred_crit_section - end crit section begun with __begin_... + * @label: label obtained from __begin_cred_crit_section + * @needput: output: bool set by __begin_cred_crit_section + * + * While the cred passed to __begin is guaranteed to not change + * and the cred and label could be passed here instead of needput + * using needput with a local var makes it easier for the compiler + * and processor to optimize and speculatively execute the comparison + * than chasing a pointer in the cred struct. + */ +static inline void __end_cred_crit_section(struct aa_label *label, + bool needput) +{ + if (unlikely(needput)) + aa_put_label(label); +} + +/** + * __begin_cred_crit_section - @cred's confining label + * @cred: current's cred to start a crit section on its label + * @needput: store whether the label needs to be put when ending crit section + * + * 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_cred_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between + * __begin_cred_crit_section() .. __end_cred_crit_section() + * + * The crit section is an optimization to avoid having to get and put + * the newest version of the label. While the cred won't change and + * hence the label it contains won't change, the newest version of the + * label can. During the crit section the newest versions of the label + * will be used until the end of the crit section. + * + * If the label has not been updated at the start of the crit section + * no refcount is taken, the cred's refcount is enough to hold the + * label for the duration of the crit section. + * + * If the label has been updated then a refcount will be taken and the + * newest version of the label will be returned. While the cred label + * and the returned label could be compared at the end of the crit + * section, needput is used because it allows better optimization by + * the compiler and the processor's speculative execution. + */ +static inline struct aa_label *__begin_cred_crit_section(const struct cred *cred, + bool *needput) +{ + struct aa_label *label = cred_label(cred); + + if (label_is_stale(label)) { + *needput = true; + return aa_get_newest_label(label); + } + + *needput = false; + return label; +} + +/** + * __end_current_label_crit_section - end crit section begun with __begin_... + * @label: label obtained from __begin_current_label_crit_section + * @needput: output: bool set by __begin_current_label_crit_section + * + * wrapper around __end_cred_crit_section() to pair nicely with + * __begin_current_label_crit_section() + */ +static inline void __end_current_label_crit_section(struct aa_label *label, + bool needput) +{ + __end_cred_crit_section(label, needput); +} + +/** + * end_current_label_crit_section - put a reference found with begin_current_label.. + * @label: label reference to put + * @needput: output: bool set by __begin_current_label_crit_section + * + * 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, + bool needput) +{ + if (label != aa_current_raw_label()) + aa_put_label(label); +} + +/** + * __begin_current_label_crit_section - current's confining label + * @needput: store whether the label needs to be put when ending crit section + * + * 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(bool *needput) +{ + return __begin_cred_crit_section(current_cred(), needput); +} + +/** + * begin_current_label_crit_section - current's confining label and update it + * @needput: store whether the label needs to be put when ending crit section + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * The returned reference must be put with end_current_label_crit_section() + * This should 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(bool *needput) +{ + struct aa_label *label = aa_current_raw_label(); + + label = __begin_current_label_crit_section(needput); + if (*needput) + aa_schedule_stale_label_replacement(); + + return label; +} + +static inline struct aa_ns *aa_get_current_ns(void) +{ + struct aa_label *label; + struct aa_ns *ns; + bool needput; + + label = __begin_current_label_crit_section(&needput); + ns = aa_get_ns(labels_ns(label)); + __end_current_label_crit_section(label, needput); + + 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..f3ffd388c --- /dev/null +++ b/security/apparmor/include/crypto.h @@ -0,0 +1,38 @@ +/* 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 +int init_profile_hash(void); +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..77f9a0ed0 --- /dev/null +++ b/security/apparmor/include/domain.h @@ -0,0 +1,33 @@ +/* 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 + +#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); + +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..1614c07fc --- /dev/null +++ b/security/apparmor/include/file.h @@ -0,0 +1,133 @@ +/* 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_policydb; +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; +}; + +/* + * 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 AA_INDEX_MASK + +#define AA_X_TYPE_MASK 0x0c000000 +#define AA_X_NONE AA_INDEX_NONE +#define AA_X_NAME 0x04000000 /* use executable name px */ +#define AA_X_TABLE 0x08000000 /* use a specified name ->n# */ + +#define AA_X_UNSAFE 0x10000000 +#define AA_X_CHILD 0x20000000 +#define AA_X_INHERIT 0x40000000 +#define AA_X_UNCONFINED 0x80000000 + +/* 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) + +int aa_audit_file(const struct cred *cred, + struct aa_profile *profile, const 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_perms *aa_lookup_condperms(kuid_t subj_uid, + struct aa_policydb *file_rules, + aa_state_t state, struct path_cond *cond); +aa_state_t aa_str_perms(struct aa_policydb *file_rules, aa_state_t start, + const char *name, struct path_cond *cond, + struct aa_perms *perms); + +int __aa_path_perm(const char *op, const struct cred *subj_cred, + 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, const struct cred *subj_cred, + struct aa_label *label, const struct path *path, + int flags, u32 request, struct path_cond *cond); + +int aa_path_link(const struct cred *subj_cred, struct aa_label *label, + struct dentry *old_dentry, const struct path *new_dir, + struct dentry *new_dentry); + +int aa_file_perm(const char *op, const struct cred *subj_cred, + struct aa_label *label, struct file *file, + u32 request, bool in_atomic); + +void aa_inherit_files(const struct cred *cred, struct files_struct *files); + + +/** + * aa_map_file_to_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..323dd071a --- /dev/null +++ b/security/apparmor/include/ipc.h @@ -0,0 +1,23 @@ +/* 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> + +#define SIGUNKNOWN 0 +#define MAXMAPPED_SIG 35 + +int aa_may_signal(const struct cred *subj_cred, struct aa_label *sender, + const struct cred *target_cred, 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..2c869b7de --- /dev/null +++ b/security/apparmor/include/label.h @@ -0,0 +1,492 @@ +/* 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; +struct aa_ruleset; + +#define LOCAL_VEC_ENTRIES 8 +#define DEFINE_VEC(T, V) \ + struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES + 1]; \ + 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_objs(struct aa_ ## T *, (N) + 1, (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 aa_common_ref count; + struct aa_label __rcu *label; +}; + +struct label_it { + int i, j; +}; + +/* struct aa_label_base - base info of label + * @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[] + * @mediates: bitmask for label_mediates + * profile: label vec when embedded in a profile FLAG_PROFILE is set + * rules: variable length rules in a profile FLAG_PROFILE is set + * vec: vector of profiles comprising the compound label + */ +struct aa_label { + struct aa_common_ref count; + struct rb_node node; + struct rcu_head rcu; + struct aa_proxy *proxy; + __counted char *hname; + long flags; + u32 secid; + int size; + u64 mediates; + union { + struct { + /* only used is the label is a profile, size of + * rules[] is determined by the profile + * profile[1] is poison or null as guard + */ + struct aa_profile *profile[2]; + DECLARE_FLEX_ARRAY(struct aa_ruleset *, rules); + }; + DECLARE_FLEX_ARRAY(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(const 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)) + + + +/* 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) + +static inline bool label_mediates(const struct aa_label *L, unsigned char C) +{ + return (L)->mediates & (((u64) 1) << (C)); +} + +static inline bool label_mediates_safe(const struct aa_label *L, unsigned char C) +{ + if (C > AA_CLASS_LAST) + return false; + return label_mediates(L, C); +} + +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(const struct aa_label *set, const struct aa_label *sub); +bool aa_label_is_unconfined_subset(const struct aa_label *set, + const struct aa_label *sub); +struct aa_profile *__aa_label_next_not_in_set(struct label_it *I, + const struct aa_label *set, + const 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_profile *aa_label_next_in_merge(struct label_it *I, + const struct aa_label *a, + const 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_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; + aa_state_t 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; + aa_state_t state; + + state = aa_dfa_match_until(stacksplitdfa, DFA_START, str, &pos); + if (!ACCEPT_TABLE(stacksplitdfa)[state]) + return NULL; + + return pos - 3; +} + + + +struct aa_perms; +struct aa_ruleset; +int aa_label_match(const struct aa_profile *profile, struct aa_ruleset *rules, + struct aa_label *label, aa_state_t 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.count)) + return l; + + return NULL; +} + +static inline struct aa_label *aa_get_label(struct aa_label *l) +{ + if (l) + kref_get(&(l->count.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.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); +} + +/** + * aa_get_newest_label_condref - find the newest version of @l + * @l: the label to check for newer versions of + * @needput: returns whether the reference needs put + * + * Returns: refcounted newest version of @l taking into account + * replacement, renames and removals + * return @l. + */ +static inline struct aa_label *aa_get_newest_label_condref(struct aa_label *l, + bool *needput) +{ + if (l && unlikely(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); + + *needput = true; + return tmp; + } + + *needput = false; + return l; +} + +static inline void aa_put_label(struct aa_label *l) +{ + if (l) + kref_put(&l->count.count, aa_label_kref); +} + +/* wrapper fn to indicate semantics of the check */ +static inline bool __aa_subj_label_is_cached(const struct aa_label *subj_label, + const struct aa_label *obj_label) +{ + return aa_label_is_subset(obj_label, subj_label); +} + + +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.count)); + + return proxy; +} + +static inline void aa_put_proxy(struct aa_proxy *proxy) +{ + if (proxy) + kref_put(&proxy->count.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..475dd71fb --- /dev/null +++ b/security/apparmor/include/lib.h @@ -0,0 +1,363 @@ +/* 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" + +extern struct aa_dfa *stacksplitdfa; + +/* + * split individual debug cases out in preparation for finer grained + * debug controls in the future. + */ +#define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args) + +#define DEBUG_NONE 0 +#define DEBUG_LABEL_ABS_ROOT 1 +#define DEBUG_LABEL 2 +#define DEBUG_DOMAIN 4 +#define DEBUG_POLICY 8 +#define DEBUG_INTERFACE 0x10 +#define DEBUG_UNPACK 0x20 +#define DEBUG_TAGS 0x40 + +#define DEBUG_ALL 0x7f /* update if new DEBUG_X added */ +#define DEBUG_PARSE_ERROR (-1) + +#define DEBUG_ON (aa_g_debug != DEBUG_NONE) +#define DEBUG_ABS_ROOT (aa_g_debug & DEBUG_LABEL_ABS_ROOT) + +#define AA_DEBUG(opt, fmt, args...) \ + do { \ + if (aa_g_debug & opt) \ + pr_warn_ratelimited("%s: " fmt, __func__, ##args); \ + } while (0) +#define AA_DEBUG_LABEL(LAB, X, fmt, args...) \ +do { \ + if ((LAB)->flags & FLAG_DEBUG1) \ + AA_DEBUG(X, fmt, ##args); \ +} while (0) + +#define AA_DEBUG_PROFILE(PROF, X, fmt...) AA_DEBUG_LABEL(&(PROF)->label, X, ##fmt) + +#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...) \ + do { \ + BUILD_BUG_ON_INVALID(X); \ + no_printk(fmt, ##args); \ + } while (0) +#endif + +int aa_parse_debug_params(const char *str); +int aa_print_debug_params(char *buffer); + +#define AA_ERROR(fmt, args...) \ + pr_err_ratelimited("AppArmor: " fmt, ##args) + +/* Flag indicating whether initialization completed */ +extern int apparmor_initialized; + +/* semantic split of scope and view */ +#define aa_in_scope(SUBJ, OBJ) \ + aa_ns_visible(SUBJ, OBJ, false) + +#define aa_in_view(SUBJ, OBJ) \ + aa_ns_visible(SUBJ, OBJ, true) + +#define label_for_each_in_scope(I, NS, L, P) \ + label_for_each_in_ns(I, NS, L, P) + +#define fn_for_each_in_scope(L, P, FN) \ + fn_for_each_in_ns(L, P, FN) + +/* fn's in lib */ +const char *skipn_spaces(const char *str, size_t n); +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; + +enum reftype { + REF_NS, + REF_PROXY, + REF_RAWDATA, +}; + +/* common reference count used by data the shows up in aafs */ +struct aa_common_ref { + struct kref count; + enum reftype reftype; +}; + +/** + * 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 aa_state_t aa_dfa_null_transition(const struct aa_dfa *dfa, + aa_state_t 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 aa_str_table_ent { + int count; + int size; + char *strs; +}; + +struct aa_str_table { + int size; + struct aa_str_table_ent *table; +}; + +bool aa_resize_str_table(struct aa_str_table *t, int newsize, gfp_t gfp); +void aa_destroy_str_table(struct aa_str_table *table); + +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 + * @hname: 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 + * NULL if all callbacks decline to specify a transition + * ERR_PTR if build @FN fails + * + * @FN must return a label or ERR_PTR on failure. + */ +#define fn_label_build(L, P, GFP, FN) \ +({ \ + __label__ __do_cleanup, __done; \ + struct aa_label *__new_ = NULL; \ + \ + 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_ = ERR_PTR(-ENOMEM); \ + goto __done; \ + } \ + __j = 0; \ + label_for_each(__i, (L), (P)) { \ + __new_ = (FN); \ + if (!__new_) \ + continue; \ + if (IS_ERR(__new_)) \ + goto __do_cleanup; \ + __lvec[__j++] = __new_; \ + } \ + if (__j == 0) \ + /* no components adding to build */ \ + goto __do_cleanup; \ + 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)); \ + if (!__new_) \ + __new_ = ERR_PTR(-ENOMEM); \ + } else \ + __new_ = aa_get_label(&__pvec[0]->label); \ + vec_cleanup(profile, __pvec, __count); \ + } else \ + __new_ = ERR_PTR(-ENOMEM); \ +__do_cleanup: \ + vec_cleanup(label, __lvec, (L)->size); \ + } else { \ + (P) = labels_profile(L); \ + __new_ = (FN); \ + } \ +__done: \ + if (IS_ERR(__new_)) \ + AA_DEBUG(DEBUG_LABEL, "label build failed %ld\n", PTR_ERR(__new_)); \ + (__new_); \ +}) + + +#define __fn_build_in_scope(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_scope(L, P, GFP, NS_FN, OTHER_FN) \ +({ \ + fn_label_build((L), (P), (GFP), \ + __fn_build_in_scope(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..f7bd7855f --- /dev/null +++ b/security/apparmor/include/match.h @@ -0,0 +1,195 @@ +/* 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 TABLE_DATAU16(TABLE) ((u16 *)((TABLE)->td_data)) +#define TABLE_DATAU32(TABLE) ((u32 *)((TABLE)->td_data)) +#define DEFAULT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) +#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) +#define NEXT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) +#define CHECK_TABLE(DFA) ((u32 *)((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]; +}; + +#define UNPACK_ARRAY(TABLE, BLOB, LEN, TTYPE, BTYPE, NTOHX) \ + do { \ + typeof(LEN) __i; \ + TTYPE *__t = (TTYPE *) TABLE; \ + BTYPE *__b = (BTYPE *) BLOB; \ + BUILD_BUG_ON(sizeof(TTYPE) != sizeof(BTYPE)); \ + if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ + memcpy(__t, __b, (LEN) * sizeof(BTYPE)); \ + else /* copy & convert from big-endian */ \ + 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); +} + +#define aa_state_t unsigned int + +struct aa_dfa *aa_dfa_unpack(const void *blob, size_t size, int flags); +aa_state_t aa_dfa_match_len(const struct aa_dfa *dfa, aa_state_t start, + const char *str, int len); +aa_state_t aa_dfa_match(const struct aa_dfa *dfa, aa_state_t start, + const char *str); +aa_state_t aa_dfa_next(const struct aa_dfa *dfa, aa_state_t state, + const char c); +aa_state_t aa_dfa_outofband_transition(const struct aa_dfa *dfa, + aa_state_t state); +aa_state_t aa_dfa_match_until(const struct aa_dfa *dfa, aa_state_t start, + const char *str, const char **retpos); +aa_state_t aa_dfa_matchn_until(const struct aa_dfa *dfa, aa_state_t start, + const char *str, int n, const char **retpos); + +void aa_dfa_free_kref(struct kref *kref); + +/* This needs to be a power of 2 */ +#define WB_HISTORY_SIZE 32 +struct match_workbuf { + unsigned int pos; + unsigned int len; + aa_state_t history[WB_HISTORY_SIZE]; +}; +#define DEFINE_MATCH_WB(N) \ +struct match_workbuf N = { \ + .pos = 0, \ + .len = 0, \ +} + +aa_state_t aa_dfa_leftmatch(const struct aa_dfa *dfa, aa_state_t 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 MARK_DIFF_ENCODE_VERIFIED 0x10000000 +#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..46834f828 --- /dev/null +++ b/security/apparmor/include/mount.h @@ -0,0 +1,60 @@ +/* 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(const struct cred *subj_cred, + struct aa_label *label, const struct path *path, + unsigned long flags, void *data); + +int aa_bind_mount(const struct cred *subj_cred, + struct aa_label *label, const struct path *path, + const char *old_name, unsigned long flags); + + +int aa_mount_change_type(const struct cred *subj_cred, + struct aa_label *label, const struct path *path, + unsigned long flags); + +int aa_move_mount_old(const struct cred *subj_cred, + struct aa_label *label, const struct path *path, + const char *old_name); +int aa_move_mount(const struct cred *subj_cred, + struct aa_label *label, const struct path *from_path, + const struct path *to_path); + +int aa_new_mount(const struct cred *subj_cred, + struct aa_label *label, const char *dev_name, + const struct path *path, const char *type, unsigned long flags, + void *data); + +int aa_umount(const struct cred *subj_cred, + struct aa_label *label, struct vfsmount *mnt, int flags); + +int aa_pivotroot(const struct cred *subj_cred, + 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..375341929 --- /dev/null +++ b/security/apparmor/include/net.h @@ -0,0 +1,120 @@ +/* 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 __rcu *label; + struct aa_label __rcu *peer; + struct aa_label __rcu *peer_lastupdate; /* ptr cmp only, no deref */ +}; + +static inline struct aa_sk_ctx *aa_sock(const struct sock *sk) +{ + return sk->sk_security + apparmor_blob_sizes.lbs_sock; +} + +#define DEFINE_AUDIT_NET(NAME, OP, CRED, 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, \ + AA_CLASS_NET, \ + OP); \ + NAME.common.u.net = &(NAME ## _net); \ + NAME.subj_cred = (CRED); \ + NAME.net.type = (T); \ + NAME.net.protocol = (P) + +#define DEFINE_AUDIT_SK(NAME, OP, CRED, SK) \ + DEFINE_AUDIT_NET(NAME, OP, CRED, SK, (SK)->sk_family, (SK)->sk_type, \ + (SK)->sk_protocol) + + +struct aa_secmark { + u8 audit; + u8 deny; + u32 secid; + char *label; +}; + +extern struct aa_sfs_entry aa_sfs_entry_network[]; +extern struct aa_sfs_entry aa_sfs_entry_networkv9[]; + +int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy, + aa_state_t state, u32 request, const struct aa_perms *p, + struct apparmor_audit_data *ad); +/* passing in state returned by XXX_mediates_AF() */ +aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state, + u32 request, u16 af, int type, int protocol, + struct aa_perms **p, const char **info); +void audit_net_cb(struct audit_buffer *ab, void *va); +int aa_profile_af_perm(struct aa_profile *profile, + struct apparmor_audit_data *ad, + u32 request, u16 family, int type, int protocol); +int aa_af_perm(const struct cred *subj_cred, 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 apparmor_audit_data *ad, + u32 request, + const struct sock *sk) +{ + return aa_profile_af_perm(profile, ad, request, sk->sk_family, + sk->sk_type, sk->sk_protocol); +} +int aa_sk_perm(const char *op, u32 request, const struct sock *sk); +int aa_label_sk_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, const struct sock *sk); +int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label, + const char *op, u32 request, + struct file *file); + +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..250812a77 --- /dev/null +++ b/security/apparmor/include/path.h @@ -0,0 +1,36 @@ +/* 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 + +#include <linux/path.h> +#include <linux/types.h> + +enum path_flags { + PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_SOCK_COND = 0x2, + 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..ee25eb8e7 --- /dev/null +++ b/security/apparmor/include/perms.h @@ -0,0 +1,220 @@ +/* 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 AA_MAY_CREATE_SQPOLL AA_MAY_CREATE +#define AA_MAY_OVERRIDE_CRED AA_MAY_APPEND +#define AA_URING_PERM_MASK (AA_MAY_OVERRIDE_CRED | AA_MAY_CREATE_SQPOLL) + +#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 deny; /* explicit deny, or conflict if allow also set */ + + u32 subtree; /* allow perm on full subtree only when allow is set */ + u32 cond; /* set only when ~allow and ~deny */ + + u32 kill; /* set only when ~allow | deny */ + u32 complain; /* accumulates only used when ~allow & ~deny */ + u32 prompt; /* accumulates only used when ~allow & ~deny */ + + u32 audit; /* set only when allow is set */ + u32 quiet; /* set only when ~allow | deny */ + u32 hide; /* set only when ~allow | deny */ + + + u32 xindex; + u32 tag; /* tag string index, if present */ + u32 label; /* label string index, if present */ +}; + +/* + * Indexes are broken into a 24 bit index and 8 bit flag. + * For the index to be valid there must be a value in the flag + */ +#define AA_INDEX_MASK 0x00ffffff +#define AA_INDEX_FLAG_MASK 0xff000000 +#define AA_INDEX_NONE 0 + +#define ALL_PERMS_MASK 0xffffffff +extern const struct aa_perms nullperms; +extern const struct aa_perms allperms; + +/** + * aa_perms_accum_raw - accumulate perms with out masking off overlapping perms + * @accum: perms struct to accumulate into + * @addend: perms struct to add to @accum + */ +static inline void aa_perms_accum_raw(struct aa_perms *accum, + const struct aa_perms *addend) +{ + accum->deny |= addend->deny; + accum->allow &= addend->allow & ~addend->deny; + accum->audit |= addend->audit & addend->allow; + accum->quiet &= addend->quiet & ~addend->allow; + accum->kill |= addend->kill & ~addend->allow; + accum->complain |= addend->complain & ~addend->allow & ~addend->deny; + accum->cond |= addend->cond & ~addend->allow & ~addend->deny; + accum->hide &= addend->hide & ~addend->allow; + accum->prompt |= addend->prompt & ~addend->allow & ~addend->deny; + accum->subtree |= addend->subtree & ~addend->deny; + + if (!accum->xindex) + accum->xindex = addend->xindex; + if (!accum->tag) + accum->tag = addend->tag; + if (!accum->label) + accum->label = addend->label; +} + +/** + * aa_perms_accum - accumulate perms, masking off overlapping perms + * @accum: perms struct to accumulate into + * @addend: perms struct to add to @accum + */ +static inline void aa_perms_accum(struct aa_perms *accum, + const struct aa_perms *addend) +{ + accum->deny |= addend->deny; + accum->allow &= addend->allow & ~accum->deny; + accum->audit |= addend->audit & accum->allow; + accum->quiet &= addend->quiet & ~accum->allow; + accum->kill |= addend->kill & ~accum->allow; + accum->complain |= addend->complain & ~accum->allow & ~accum->deny; + accum->cond |= addend->cond & ~accum->allow & ~accum->deny; + accum->hide &= addend->hide & ~accum->allow; + accum->prompt |= addend->prompt & ~accum->allow & ~accum->deny; + accum->subtree &= addend->subtree & ~accum->deny; + + if (!accum->xindex) + accum->xindex = addend->xindex; + if (!accum->tag) + accum->tag = addend->tag; + if (!accum->label) + accum->label = addend->label; +} + +#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))) + + +extern struct aa_perms default_perms; + + +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(const struct aa_profile *profile, + struct aa_perms *perms); +void aa_perms_accum(struct aa_perms *accum, const struct aa_perms *addend); +void aa_perms_accum_raw(struct aa_perms *accum, const struct aa_perms *addend); +void aa_profile_match_label(const struct aa_profile *profile, + struct aa_ruleset *rules, struct aa_label *label, + int type, u32 request, struct aa_perms *perms); +int aa_check_perms(struct aa_profile *profile, const struct aa_perms *perms, + u32 request, struct apparmor_audit_data *ad, + 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..0dcbf8cf1 --- /dev/null +++ b/security/apparmor/include/policy.h @@ -0,0 +1,457 @@ +/* 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 "perms.h" +#include "resource.h" + + +struct aa_ns; + +extern int unprivileged_userns_apparmor_policy; +extern int aa_unprivileged_unconfined_restricted; + +extern const char *const aa_profile_mode_names[]; + +#define PROFILE_MODE(_profile, _mode) \ + ((aa_g_profile_mode == (_mode)) || \ + ((_profile)->mode == (_mode))) + +#define COMPLAIN_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_COMPLAIN) + +#define USER_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_USER) + +#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) + +/* flags in the dfa accept2 table */ +enum dfa_accept_flags { + ACCEPT_FLAG_OWNER = 1, +}; + +/* + * 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 */ + APPARMOR_USER, /* modified complain mode to userspace */ + PROFILE_MODE_NAMES_COUNT /* Must be last entry */ +}; + + +struct aa_tags_header { + u32 mask; /* bit mask matching permissions */ + u32 count; /* number of strings per entry */ + u32 size; /* size of all strings covered by count */ + u32 tags; /* index into string table */ +}; + +struct aa_tags_struct { + struct { + u32 size; /* number of entries in tagsets */ + u32 *table; /* indexes into headers & strs */ + } sets; + struct { + u32 size; /* number of headers == num of strs */ + struct aa_tags_header *table; + } hdrs; + struct aa_str_table strs; +}; + +/* struct aa_policydb - match engine for a policy + * @count: refcount for the pdb + * @dfa: dfa pattern match + * @perms: table of permissions + * @size: number of entries in @perms + * @trans: table of strings, index by x + * @tags: table of tags that perms->tag indexes + * @start:_states to start in for each class + * start: set of start states for the different classes of data + */ +struct aa_policydb { + struct kref count; + struct aa_dfa *dfa; + struct { + struct aa_perms *perms; + u32 size; + }; + struct aa_str_table trans; + struct aa_tags_struct tags; + aa_state_t start[AA_CLASS_LAST + 1]; +}; + +extern struct aa_policydb *nullpdb; + +void aa_destroy_tags(struct aa_tags_struct *tags); +struct aa_policydb *aa_alloc_pdb(gfp_t gfp); +void aa_pdb_free_kref(struct kref *kref); + +/** + * aa_get_pdb - increment refcount on @pdb + * @pdb: policydb (MAYBE NULL) + * + * Returns: pointer to @pdb if @pdb is NULL will return NULL + * Requires: @pdb must be held with valid refcount when called + */ +static inline struct aa_policydb *aa_get_pdb(struct aa_policydb *pdb) +{ + if (pdb) + kref_get(&(pdb->count)); + + return pdb; +} + +/** + * aa_put_pdb - put a pdb refcount + * @pdb: pdb to put refcount (MAYBE NULL) + * + * Requires: if @pdb != NULL that a valid refcount be held + */ +static inline void aa_put_pdb(struct aa_policydb *pdb) +{ + if (pdb) + kref_put(&pdb->count, aa_pdb_free_kref); +} + +/* lookup perm that doesn't have and object conditional */ +static inline struct aa_perms *aa_lookup_perms(struct aa_policydb *policy, + aa_state_t state) +{ + unsigned int index = ACCEPT_TABLE(policy->dfa)[state]; + + if (!(policy->perms)) + return &default_perms; + + return &(policy->perms[index]); +} + +/* 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_ruleset - data covering mediation rules + * @list: list the rule is on + * @size: the memory consumed by this ruleset + * @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 + * @secmark_count: number of secmark entries + * @secmark: secmark label match info + */ +struct aa_ruleset { + int size; + + /* TODO: merge policy and file */ + struct aa_policydb *policy; + struct aa_policydb *file; + struct aa_caps caps; + + struct aa_rlimit rlimits; + + int secmark_count; + struct aa_secmark *secmark; +}; + + +/* struct aa_attachment - data and rules for a profiles attachment + * @list: + * @xmatch_str: human readable attachment string + * @xmatch: optional extended matching for unconfined executables names + * @xmatch_len: xmatch prefix len, used to determine xmatch priority + * @xattr_count: number of xattrs in table + * @xattrs: table of xattrs + */ +struct aa_attachment { + const char *xmatch_str; + struct aa_policydb *xmatch; + unsigned int xmatch_len; + int xattr_count; + char **xattrs; +}; + +/* struct aa_profile - basic confinement data + * @base - base components of the profile (name, refcount, lists, lock ...) + * @parent: parent of profile + * @ns: namespace the profile is in + * @rename: optional profile name that this profile renamed + * + * @audit: the auditing mode of the profile + * @mode: the enforcement mode of the profile + * @path_flags: flags controlling path generation behavior + * @signal: the signal that should be used when kill is used + * @disconnected: what to prepend if attach_disconnected is specified + * @attach: attachment rules for the profile + * @rules: rules to be enforced + * + * learning_cache: the accesses learned in complain mode + * raw_data: rawdata of the loaded profile policy + * hash: cryptographic hash of the profile + * @dents: dentries for the profiles file entries in apparmorfs + * @dirname: name of the profile dir in apparmorfs + * @dents: set of dentries associated with the profile + * @data: hashtable for free-form policy aa_data + * @label - label this profile is an extension of + * @rules - label with the rule vec on its end + * + * 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; + + enum audit_mode audit; + long mode; + u32 path_flags; + int signal; + const char *disconnected; + + struct aa_attachment attach; + + struct aa_loaddata *rawdata; + unsigned char *hash; + char *dirname; + struct dentry *dents[AAFS_PROF_SIZEOF]; + struct rhashtable *data; + + int n_rules; + /* special - variable length must be last entry in profile */ + 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) + +struct aa_ruleset *aa_alloc_ruleset(gfp_t gfp); +struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy, + gfp_t gfp); +struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name, + gfp_t gfp); +struct aa_profile *__aa_new_learning_profile(struct aa_profile *parent, + bool hat, const char *base, + gfp_t gfp); +struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp); +void aa_free_profile(struct aa_profile *profile); +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_fqlookupn_profile(struct aa_label *base, + const char *fqname, size_t n); + +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label, + u32 mask, struct aa_loaddata *udata, + char *compressed_profile, size_t compressed_size); +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 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 aa_state_t RULE_MEDIATES(struct aa_ruleset *rules, + unsigned char class) +{ + if (class <= AA_CLASS_LAST) + return rules->policy->start[class]; + else + return aa_dfa_match_len(rules->policy->dfa, + rules->policy->start[0], &class, 1); +} + +static inline aa_state_t RULE_MEDIATES_v9NET(struct aa_ruleset *rules) +{ + return RULE_MEDIATES(rules, AA_CLASS_NETV9); +} + +static inline aa_state_t RULE_MEDIATES_NET(struct aa_ruleset *rules) +{ + /* can not use RULE_MEDIATE_v9AF here, because AF match fail + * can not be distiguished from class match fail, and we only + * fallback to checking older class on class match failure + */ + aa_state_t state = RULE_MEDIATES(rules, AA_CLASS_NETV9); + + /* fallback and check v7/8 if v9 is NOT mediated */ + if (!state) + state = RULE_MEDIATES(rules, AA_CLASS_NET); + return state; +} + +static inline aa_state_t RULE_MEDIATES_UNIX(struct aa_ruleset *rules) +{ + return RULE_MEDIATES_v9NET(rules); +} + + +void aa_compute_profile_mediates(struct aa_profile *profile); +static inline bool profile_mediates(struct aa_profile *profile, + unsigned char class) +{ + return label_mediates(&profile->label, class); +} + +static inline bool profile_mediates_safe(struct aa_profile *profile, + unsigned char class) +{ + return label_mediates_safe(&profile->label, class); +} + +/** + * 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.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.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.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.count, aa_label_kref); +} + +static inline int AUDIT_MODE(const struct aa_profile *profile) +{ + if (aa_g_audit != AUDIT_NORMAL) + return aa_g_audit; + + return profile->audit; +} + +bool aa_policy_view_capable(const struct cred *subj_cred, + struct aa_label *label, struct aa_ns *ns); +bool aa_policy_admin_capable(const struct cred *subj_cred, + struct aa_label *label, struct aa_ns *ns); +int aa_may_manage_policy(const struct cred *subj_cred, + struct aa_label *label, struct aa_ns *ns, + const struct cred *ocred, 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_compat.h b/security/apparmor/include/policy_compat.h new file mode 100644 index 000000000..af0e17433 --- /dev/null +++ b/security/apparmor/include/policy_compat.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * Code to provide backwards compatibility with older policy versions, + * by converting/mapping older policy formats into the newer internal + * formats. + * + * Copyright 2022 Canonical Ltd. + */ + +#ifndef __POLICY_COMPAT_H +#define __POLICY_COMPAT_H + +#include "policy.h" + +#define K_ABI_MASK 0x3ff +#define FORCE_COMPLAIN_FLAG 0x800 +#define VERSION_LT(X, Y) (((X) & K_ABI_MASK) < ((Y) & K_ABI_MASK)) +#define VERSION_LE(X, Y) (((X) & K_ABI_MASK) <= ((Y) & K_ABI_MASK)) +#define VERSION_GT(X, Y) (((X) & K_ABI_MASK) > ((Y) & K_ABI_MASK)) + +#define v5 5 /* base version */ +#define v6 6 /* per entry policydb mediation check */ +#define v7 7 +#define v8 8 /* full network masking */ +#define v9 9 /* xbits are used as permission bits in policydb */ + +int aa_compat_map_xmatch(struct aa_policydb *policy); +int aa_compat_map_policy(struct aa_policydb *policy, u32 version); +int aa_compat_map_file(struct aa_policydb *policy); + +#endif /* __POLICY_COMPAT_H */ diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h new file mode 100644 index 000000000..cc6e84151 --- /dev/null +++ b/security/apparmor/include/policy_ns.h @@ -0,0 +1,153 @@ +/* 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" + +/* Match max depth of user namespaces */ +#define MAX_NS_DEPTH 32 + +/* 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); + +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)); +} + +#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..c01f6885d --- /dev/null +++ b/security/apparmor/include/policy_unpack.h @@ -0,0 +1,217 @@ +/* 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> + +#include "lib.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 +#define PACKED_MODE_USER 4 + +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 + * @count: inode/filesystem refcount - use aa_get_i_loaddata() + * @pcount: profile refcount - use aa_get_profile_loaddata() + * @list: list the loaddata is on + * @work: used to do a delayed cleanup + * @dents: refs to dents created in aafs + * @ns: the namespace this loaddata was loaded into + * @name: + * @size: the size of the data that was loaded + * @compressed_size: the size of the data when it is compressed + * @revision: unique revision count that this data was loaded as + * @abi: the abi number the loaddata uses + * @hash: a hash of the loaddata, used to help dedup data + * + * There is no loaddata ref for being on ns->rawdata_list, so + * @ns->lock must be held when walking the list. Dentries and + * inode opens hold refs on @count; profiles hold refs on @pcount. + * When the last @pcount drops, do_ploaddata_rmfs() removes the + * fs entries and drops the associated @count ref. + */ +struct aa_loaddata { + struct aa_common_ref count; + struct kref pcount; + 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, + char *compressed_data, size_t compressed_size); + +/** + * aa_get_i_loaddata - get a reference count from a counted data reference + * @data: reference to get a count on + * + * Returns: pointer 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_i_loaddata(struct aa_loaddata *data) +{ + + if (data) + kref_get(&(data->count.count)); + return data; +} + + +/** + * aa_get_profile_loaddata - get a profile reference count on loaddata + * @data: reference to get a count on + * + * Returns: pointer to reference + * Requires: @data to have a valid reference count on it. + */ +static inline struct aa_loaddata * +aa_get_profile_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_get(&(data->pcount)); + return data; +} + +/** + * aa_get_profile_loaddata_not0 - get a profile reference count if not zero + * @data: reference to get a count on + * + * Like aa_get_profile_loaddata(), but safe to call on an entry that may + * be on a list (e.g. ns->rawdata_list) where the last pcount has already + * dropped and the deferred cleanup has not yet run. + * + * Returns: pointer to reference, or %NULL if @data is NULL or its + * profile refcount has already reached zero. + */ +static inline struct aa_loaddata * +aa_get_profile_loaddata_not0(struct aa_loaddata *data) +{ + if (data && kref_get_unless_zero(&data->pcount)) + return data; + return NULL; +} + +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); +void aa_ploaddata_kref(struct kref *kref); +struct aa_loaddata *aa_loaddata_alloc(size_t size); +static inline void aa_put_i_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_put(&data->count.count, aa_loaddata_kref); +} + +static inline void aa_put_profile_loaddata(struct aa_loaddata *data) +{ + if (data) + kref_put(&data->pcount, aa_ploaddata_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); +bool aa_unpack_array(struct aa_ext *e, const char *name, u16 *size); +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..56acd1bdb --- /dev/null +++ b/security/apparmor/include/procattr.h @@ -0,0 +1,19 @@ +/* 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 + +#include "label.h" + +int aa_getprocattr(struct aa_label *label, char **string, bool newline); +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..ad2c0da8e --- /dev/null +++ b/security/apparmor/include/resource.h @@ -0,0 +1,47 @@ +/* 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(const struct cred *subj_cred, 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..6025d3849 --- /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, struct lsm_context *cp); +int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp); +int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid); +void apparmor_release_secctx(struct lsm_context *cp); + + +int aa_alloc_secid(struct aa_label *label, gfp_t gfp); +void aa_free_secid(u32 secid); + +#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..c772668cd --- /dev/null +++ b/security/apparmor/include/sig_names.h @@ -0,0 +1,97 @@ +#include <linux/signal.h> +#include "signal.h" + +/* 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/signal.h b/security/apparmor/include/signal.h new file mode 100644 index 000000000..729763fa7 --- /dev/null +++ b/security/apparmor/include/signal.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * AppArmor security module + * + * This file contains AppArmor ipc mediation function definitions. + * + * Copyright 2023 Canonical Ltd. + */ + +#ifndef __AA_SIGNAL_H +#define __AA_SIGNAL_H + +#define SIGUNKNOWN 0 +#define MAXMAPPED_SIG 35 + +#define MAXMAPPED_SIGNAME (MAXMAPPED_SIG + 1) +#define SIGRT_BASE 128 + +#endif /* __AA_SIGNAL_H */ diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h new file mode 100644 index 000000000..a8030ed78 --- /dev/null +++ b/security/apparmor/include/task.h @@ -0,0 +1,117 @@ +/* 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 + +#include <linux/sched.h> + +#include "audit.h" +#include "label.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 + * @label_replacement_tw: for aa_schedule_stale_label_replacement() + * @label_replacement_pending: is @label_replacement_tw pending? + * + * When changing this, check if aa_dup_task_ctx() needs to be updated. + */ +struct aa_task_ctx { + struct aa_label *nnp; + struct aa_label *onexec; + struct aa_label *previous; + u64 token; + struct callback_head label_replacement_tw; + bool label_replacement_pending; +}; + +int aa_replace_current_label(struct aa_label *label); +void aa_schedule_stale_label_replacement(void); +void 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->nnp = aa_get_label(old->nnp); + new->onexec = aa_get_label(old->onexec); + new->previous = aa_get_label(old->previous); + new->token = old->token; +} + +/** + * 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(const struct cred *tracer_cred, struct aa_label *tracer, + const struct cred *tracee_cred, struct aa_label *tracee, + u32 request); + + + +#define AA_USERNS_CREATE 8 + +int aa_profile_ns_perm(struct aa_profile *profile, + struct apparmor_audit_data *ad, u32 request); + +#endif /* __AA_TASK_H */ |
