diff --git a/MAINTAINERS b/MAINTAINERS index 43fc15a281d1..540104bc4f06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14734,7 +14734,7 @@ NETWORKING [LABELED] (NetLabel, Labeled IPsec, SECMARK) M: Paul Moore L: netdev@vger.kernel.org L: linux-security-module@vger.kernel.org -S: Maintained +S: Supported W: https://github.com/netlabel F: Documentation/netlabel/ F: include/net/calipso.h diff --git a/arch/Kconfig b/arch/Kconfig index e1faf3e3637b..d6a68656950e 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1214,13 +1214,6 @@ config COMPAT_32BIT_TIME config ARCH_NO_PREEMPT bool -config ARCH_EPHEMERAL_INODES - def_bool n - help - An arch should select this symbol if it doesn't keep track of inode - instances on its own, but instead relies on something else (e.g. the - host kernel for an UML kernel). - config ARCH_SUPPORTS_RT bool diff --git a/arch/um/Kconfig b/arch/um/Kconfig index 887cfb636c26..b5e179360534 100644 --- a/arch/um/Kconfig +++ b/arch/um/Kconfig @@ -5,7 +5,6 @@ menu "UML-specific options" config UML bool default y - select ARCH_EPHEMERAL_INODES select ARCH_HAS_CPU_FINALIZE_INIT select ARCH_HAS_FORTIFY_SOURCE select ARCH_HAS_GCOV_PROFILE_ALL diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 1033fbdfdbec..983ce34115d5 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1517,7 +1517,7 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset) phdr->p_filesz = sz; phdr->p_memsz = 0; phdr->p_flags = 0; - phdr->p_align = 0; + phdr->p_align = 4; } static void fill_note(struct memelfnote *note, const char *name, int type, @@ -1773,7 +1773,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, /* * NT_PRSTATUS is the one special case, because the regset data * goes into the pr_reg field inside the note contents, rather - * than being the whole note contents. We fill the reset in here. + * than being the whole note contents. We fill the regset in here. * We assume that regset 0 is NT_PRSTATUS. */ fill_prstatus(&t->prstatus.common, t->task, signr); diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 05a1471d5283..1c6c5832af86 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -743,12 +743,12 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params, struct elf32_fdpic_loadmap *loadmap; #ifdef CONFIG_MMU struct elf32_fdpic_loadseg *mseg; + unsigned long load_addr; #endif struct elf32_fdpic_loadseg *seg; struct elf32_phdr *phdr; - unsigned long load_addr, stop; unsigned nloads, tmp; - size_t size; + unsigned long stop; int loop, ret; /* allocate a load map table */ @@ -760,8 +760,7 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params, if (nloads == 0) return -ELIBBAD; - size = sizeof(*loadmap) + nloads * sizeof(*seg); - loadmap = kzalloc(size, GFP_KERNEL); + loadmap = kzalloc(struct_size(loadmap, segs, nloads), GFP_KERNEL); if (!loadmap) return -ENOMEM; @@ -770,9 +769,6 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params, loadmap->version = ELF32_FDPIC_LOADMAP_VERSION; loadmap->nsegs = nloads; - load_addr = params->load_addr; - seg = loadmap->segs; - /* map the requested LOADs into the memory space */ switch (params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) { case ELF_FDPIC_FLAG_CONSTDISP: @@ -1269,7 +1265,7 @@ static inline void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offs phdr->p_filesz = sz; phdr->p_memsz = 0; phdr->p_flags = 0; - phdr->p_align = 0; + phdr->p_align = 4; return; } diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index 69cb796f6270..0239e3af3945 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -65,6 +65,7 @@ struct hostfs_stat { unsigned long long blocks; unsigned int maj; unsigned int min; + dev_t dev; }; extern int stat_file(const char *path, struct hostfs_stat *p, int fd); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 87998df499f4..46387090eb76 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -26,6 +26,7 @@ struct hostfs_inode_info { fmode_t mode; struct inode vfs_inode; struct mutex open_mutex; + dev_t dev; }; static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) @@ -182,14 +183,6 @@ static char *follow_link(char *link) return ERR_PTR(n); } -static struct inode *hostfs_iget(struct super_block *sb) -{ - struct inode *inode = new_inode(sb); - if (!inode) - return ERR_PTR(-ENOMEM); - return inode; -} - static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) { /* @@ -228,6 +221,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb) return NULL; hi->fd = -1; hi->mode = 0; + hi->dev = 0; inode_init_once(&hi->vfs_inode); mutex_init(&hi->open_mutex); return &hi->vfs_inode; @@ -240,6 +234,7 @@ static void hostfs_evict_inode(struct inode *inode) if (HOSTFS_I(inode)->fd != -1) { close_file(&HOSTFS_I(inode)->fd); HOSTFS_I(inode)->fd = -1; + HOSTFS_I(inode)->dev = 0; } } @@ -265,6 +260,7 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root) static const struct super_operations hostfs_sbops = { .alloc_inode = hostfs_alloc_inode, .free_inode = hostfs_free_inode, + .drop_inode = generic_delete_inode, .evict_inode = hostfs_evict_inode, .statfs = hostfs_statfs, .show_options = hostfs_show_options, @@ -512,18 +508,31 @@ static const struct address_space_operations hostfs_aops = { .write_end = hostfs_write_end, }; -static int read_name(struct inode *ino, char *name) +static int hostfs_inode_update(struct inode *ino, const struct hostfs_stat *st) { + set_nlink(ino, st->nlink); + i_uid_write(ino, st->uid); + i_gid_write(ino, st->gid); + ino->i_atime = + (struct timespec64){ st->atime.tv_sec, st->atime.tv_nsec }; + ino->i_mtime = + (struct timespec64){ st->mtime.tv_sec, st->mtime.tv_nsec }; + ino->i_ctime = + (struct timespec64){ st->ctime.tv_sec, st->ctime.tv_nsec }; + ino->i_size = st->size; + ino->i_blocks = st->blocks; + return 0; +} + +static int hostfs_inode_set(struct inode *ino, void *data) +{ + struct hostfs_stat *st = data; dev_t rdev; - struct hostfs_stat st; - int err = stat_file(name, &st, -1); - if (err) - return err; /* Reencode maj and min with the kernel encoding.*/ - rdev = MKDEV(st.maj, st.min); + rdev = MKDEV(st->maj, st->min); - switch (st.mode & S_IFMT) { + switch (st->mode & S_IFMT) { case S_IFLNK: ino->i_op = &hostfs_link_iops; break; @@ -535,7 +544,7 @@ static int read_name(struct inode *ino, char *name) case S_IFBLK: case S_IFIFO: case S_IFSOCK: - init_special_inode(ino, st.mode & S_IFMT, rdev); + init_special_inode(ino, st->mode & S_IFMT, rdev); ino->i_op = &hostfs_iops; break; case S_IFREG: @@ -547,17 +556,42 @@ static int read_name(struct inode *ino, char *name) return -EIO; } - ino->i_ino = st.ino; - ino->i_mode = st.mode; - set_nlink(ino, st.nlink); - i_uid_write(ino, st.uid); - i_gid_write(ino, st.gid); - ino->i_atime = (struct timespec64){ st.atime.tv_sec, st.atime.tv_nsec }; - ino->i_mtime = (struct timespec64){ st.mtime.tv_sec, st.mtime.tv_nsec }; - ino->i_ctime = (struct timespec64){ st.ctime.tv_sec, st.ctime.tv_nsec }; - ino->i_size = st.size; - ino->i_blocks = st.blocks; - return 0; + HOSTFS_I(ino)->dev = st->dev; + ino->i_ino = st->ino; + ino->i_mode = st->mode; + return hostfs_inode_update(ino, st); +} + +static int hostfs_inode_test(struct inode *inode, void *data) +{ + const struct hostfs_stat *st = data; + + return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == st->dev; +} + +static struct inode *hostfs_iget(struct super_block *sb, char *name) +{ + struct inode *inode; + struct hostfs_stat st; + int err = stat_file(name, &st, -1); + + if (err) + return ERR_PTR(err); + + inode = iget5_locked(sb, st.ino, hostfs_inode_test, hostfs_inode_set, + &st); + if (!inode) + return ERR_PTR(-ENOMEM); + + if (inode->i_state & I_NEW) { + unlock_new_inode(inode); + } else { + spin_lock(&inode->i_lock); + hostfs_inode_update(inode, &st); + spin_unlock(&inode->i_lock); + } + + return inode; } static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir, @@ -565,62 +599,48 @@ static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir, { struct inode *inode; char *name; - int error, fd; + int fd; - inode = hostfs_iget(dir->i_sb); - if (IS_ERR(inode)) { - error = PTR_ERR(inode); - goto out; - } - - error = -ENOMEM; name = dentry_name(dentry); if (name == NULL) - goto out_put; + return -ENOMEM; fd = file_create(name, mode & 0777); - if (fd < 0) - error = fd; - else - error = read_name(inode, name); + if (fd < 0) { + __putname(name); + return fd; + } + inode = hostfs_iget(dir->i_sb, name); __putname(name); - if (error) - goto out_put; + if (IS_ERR(inode)) + return PTR_ERR(inode); HOSTFS_I(inode)->fd = fd; HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE; d_instantiate(dentry, inode); return 0; - - out_put: - iput(inode); - out: - return error; } static struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, unsigned int flags) { - struct inode *inode; + struct inode *inode = NULL; char *name; - int err; - inode = hostfs_iget(ino->i_sb); - if (IS_ERR(inode)) - goto out; - - err = -ENOMEM; name = dentry_name(dentry); - if (name) { - err = read_name(inode, name); - __putname(name); + if (name == NULL) + return ERR_PTR(-ENOMEM); + + inode = hostfs_iget(ino->i_sb, name); + __putname(name); + if (IS_ERR(inode)) { + if (PTR_ERR(inode) == -ENOENT) + inode = NULL; + else + return ERR_CAST(inode); } - if (err) { - iput(inode); - inode = (err == -ENOENT) ? NULL : ERR_PTR(err); - } - out: + return d_splice_alias(inode, dentry); } @@ -704,35 +724,23 @@ static int hostfs_mknod(struct mnt_idmap *idmap, struct inode *dir, char *name; int err; - inode = hostfs_iget(dir->i_sb); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - goto out; - } - - err = -ENOMEM; name = dentry_name(dentry); if (name == NULL) - goto out_put; + return -ENOMEM; err = do_mknod(name, mode, MAJOR(dev), MINOR(dev)); - if (err) - goto out_free; + if (err) { + __putname(name); + return err; + } - err = read_name(inode, name); + inode = hostfs_iget(dir->i_sb, name); __putname(name); - if (err) - goto out_put; + if (IS_ERR(inode)) + return PTR_ERR(inode); d_instantiate(dentry, inode); return 0; - - out_free: - __putname(name); - out_put: - iput(inode); - out: - return err; } static int hostfs_rename2(struct mnt_idmap *idmap, @@ -929,49 +937,40 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) sb->s_maxbytes = MAX_LFS_FILESIZE; err = super_setup_bdi(sb); if (err) - goto out; + return err; /* NULL is printed as '(null)' by printf(): avoid that. */ if (req_root == NULL) req_root = ""; - err = -ENOMEM; sb->s_fs_info = host_root_path = kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root); if (host_root_path == NULL) - goto out; + return -ENOMEM; - root_inode = new_inode(sb); - if (!root_inode) - goto out; - - err = read_name(root_inode, host_root_path); - if (err) - goto out_put; + root_inode = hostfs_iget(sb, host_root_path); + if (IS_ERR(root_inode)) + return PTR_ERR(root_inode); if (S_ISLNK(root_inode->i_mode)) { - char *name = follow_link(host_root_path); - if (IS_ERR(name)) { - err = PTR_ERR(name); - goto out_put; - } - err = read_name(root_inode, name); + char *name; + + iput(root_inode); + name = follow_link(host_root_path); + if (IS_ERR(name)) + return PTR_ERR(name); + + root_inode = hostfs_iget(sb, name); kfree(name); - if (err) - goto out_put; + if (IS_ERR(root_inode)) + return PTR_ERR(root_inode); } - err = -ENOMEM; sb->s_root = d_make_root(root_inode); if (sb->s_root == NULL) - goto out; + return -ENOMEM; return 0; - -out_put: - iput(root_inode); -out: - return err; } static struct dentry *hostfs_read_sb(struct file_system_type *type, diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 5ecc4706172b..840619e39a1a 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -36,6 +36,7 @@ static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p) p->blocks = buf->st_blocks; p->maj = os_major(buf->st_rdev); p->min = os_minor(buf->st_rdev); + p->dev = buf->st_dev; } int stat_file(const char *path, struct hostfs_stat *p, int fd) diff --git a/fs/proc/vmcore.c b/fs/proc/vmcore.c index 03f5963914a1..cb80a7703d58 100644 --- a/fs/proc/vmcore.c +++ b/fs/proc/vmcore.c @@ -877,7 +877,7 @@ static int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz, phdr.p_offset = roundup(note_off, PAGE_SIZE); phdr.p_vaddr = phdr.p_paddr = 0; phdr.p_filesz = phdr.p_memsz = phdr_sz; - phdr.p_align = 0; + phdr.p_align = 4; /* Add merged PT_NOTE program header*/ tmp = elfptr + sizeof(Elf64_Ehdr); @@ -1068,7 +1068,7 @@ static int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz, phdr.p_offset = roundup(note_off, PAGE_SIZE); phdr.p_vaddr = phdr.p_paddr = 0; phdr.p_filesz = phdr.p_memsz = phdr_sz; - phdr.p_align = 0; + phdr.p_align = 4; /* Add merged PT_NOTE program header*/ tmp = elfptr + sizeof(Elf32_Ehdr); diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index ade66dbe5f39..2f625e1fa8d8 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -875,7 +875,7 @@ fail_out: return err; } -static int ramoops_remove(struct platform_device *pdev) +static void ramoops_remove(struct platform_device *pdev) { struct ramoops_context *cxt = &oops_cxt; @@ -885,8 +885,6 @@ static int ramoops_remove(struct platform_device *pdev) cxt->pstore.bufsize = 0; ramoops_free_przs(cxt); - - return 0; } static const struct of_device_id dt_match[] = { @@ -896,7 +894,7 @@ static const struct of_device_id dt_match[] = { static struct platform_driver ramoops_driver = { .probe = ramoops_probe, - .remove = ramoops_remove, + .remove_new = ramoops_remove, .driver = { .name = "ramoops", .of_match_table = dt_match, diff --git a/fs/pstore/ram_core.c b/fs/pstore/ram_core.c index 966191d3a5ba..85aaf0fc6d7d 100644 --- a/fs/pstore/ram_core.c +++ b/fs/pstore/ram_core.c @@ -599,6 +599,8 @@ struct persistent_ram_zone *persistent_ram_new(phys_addr_t start, size_t size, raw_spin_lock_init(&prz->buffer_lock); prz->flags = flags; prz->label = kstrdup(label, GFP_KERNEL); + if (!prz->label) + goto err; ret = persistent_ram_buffer_map(start, size, prz, memtype); if (ret) diff --git a/fs/reiserfs/xattr_security.c b/fs/reiserfs/xattr_security.c index 6e0a099dd788..078dd8cc312f 100644 --- a/fs/reiserfs/xattr_security.c +++ b/fs/reiserfs/xattr_security.c @@ -67,6 +67,7 @@ int reiserfs_security_init(struct inode *dir, struct inode *inode, sec->name = NULL; sec->value = NULL; + sec->length = 0; /* Don't add selinux attributes on xattrs - they'll never get used */ if (IS_PRIVATE(dir)) diff --git a/include/linux/audit.h b/include/linux/audit.h index 31086a72e32a..6a3a9e122bb5 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -130,8 +130,6 @@ extern unsigned compat_dir_class[]; extern unsigned compat_chattr_class[]; extern unsigned compat_signal_class[]; -extern int audit_classify_compat_syscall(int abi, unsigned syscall); - /* audit_names->type values */ #define AUDIT_TYPE_UNKNOWN 0 /* we don't know yet */ #define AUDIT_TYPE_NORMAL 1 /* a "normal" audit record */ diff --git a/include/linux/audit_arch.h b/include/linux/audit_arch.h index 8fdb1afe251a..0e34d673ef17 100644 --- a/include/linux/audit_arch.h +++ b/include/linux/audit_arch.h @@ -21,4 +21,6 @@ enum auditsc_class_t { AUDITSC_NVALS /* count */ }; +extern int audit_classify_compat_syscall(int abi, unsigned syscall); + #endif diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 6bb55e61e8e8..7308a1a7599b 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -343,6 +343,7 @@ LSM_HOOK(void, LSM_RET_VOID, sctp_sk_clone, struct sctp_association *asoc, struct sock *sk, struct sock *newsk) LSM_HOOK(int, 0, sctp_assoc_established, struct sctp_association *asoc, struct sk_buff *skb) +LSM_HOOK(int, 0, mptcp_add_subflow, struct sock *sk, struct sock *ssk) #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_INFINIBAND diff --git a/include/linux/security.h b/include/linux/security.h index e2734e9e44d5..32828502f09e 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1465,6 +1465,7 @@ void security_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk, struct sock *newsk); int security_sctp_assoc_established(struct sctp_association *asoc, struct sk_buff *skb); +int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk); #else /* CONFIG_SECURITY_NETWORK */ static inline int security_unix_stream_connect(struct sock *sock, @@ -1692,6 +1693,11 @@ static inline int security_sctp_assoc_established(struct sctp_association *asoc, { return 0; } + +static inline int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk) +{ + return 0; +} #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_INFINIBAND diff --git a/include/uapi/linux/capability.h b/include/uapi/linux/capability.h index 3d61a0ae055d..5bb906098697 100644 --- a/include/uapi/linux/capability.h +++ b/include/uapi/linux/capability.h @@ -41,11 +41,12 @@ typedef struct __user_cap_header_struct { int pid; } __user *cap_user_header_t; -typedef struct __user_cap_data_struct { +struct __user_cap_data_struct { __u32 effective; __u32 permitted; __u32 inheritable; -} __user *cap_user_data_t; +}; +typedef struct __user_cap_data_struct __user *cap_user_data_t; #define VFS_CAP_REVISION_MASK 0xFF000000 diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index ac3da855fb19..4d1c8d46e7f0 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -372,7 +372,8 @@ typedef struct elf64_shdr { * Notes used in ET_CORE. Architectures export some of the arch register sets * using the corresponding note types via the PTRACE_GETREGSET and * PTRACE_SETREGSET requests. - * The note name for all these is "LINUX". + * The note name for these types is "LINUX", except NT_PRFPREG that is named + * "CORE". */ #define NT_PRSTATUS 1 #define NT_PRFPREG 2 diff --git a/kernel/audit.h b/kernel/audit.h index c57b008b9914..94738bce40b2 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -259,8 +259,8 @@ extern struct tty_struct *audit_get_tty(void); extern void audit_put_tty(struct tty_struct *tty); /* audit watch/mark/tree functions */ -#ifdef CONFIG_AUDITSYSCALL extern unsigned int audit_serial(void); +#ifdef CONFIG_AUDITSYSCALL extern int auditsc_get_stamp(struct audit_context *ctx, struct timespec64 *t, unsigned int *serial); diff --git a/kernel/capability.c b/kernel/capability.c index 3e058f41df32..1a2795102ae4 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -467,6 +467,7 @@ EXPORT_SYMBOL(file_ns_capable); /** * privileged_wrt_inode_uidgid - Do capabilities in the namespace work over the inode? * @ns: The user namespace in question + * @idmap: idmap of the mount @inode was found from * @inode: The inode in question * * Return true if the inode uid and gid are within the namespace. @@ -481,6 +482,7 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, /** * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped + * @idmap: idmap of the mount @inode was found from * @inode: The inode in question * @cap: The capability in question * diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index d9c8b21c6076..8ff5c9fab55b 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -1668,6 +1668,10 @@ int mptcp_subflow_create_socket(struct sock *sk, unsigned short family, lock_sock_nested(sf->sk, SINGLE_DEPTH_NESTING); + err = security_mptcp_add_subflow(sk, sf->sk); + if (err) + goto release_ssk; + /* the newly created socket has to be in the same cgroup as its parent */ mptcp_attach_cgroup(sk, sf->sk); @@ -1680,6 +1684,8 @@ int mptcp_subflow_create_socket(struct sock *sk, unsigned short family, get_net_track(net, &sf->sk->ns_tracker, GFP_KERNEL); sock_inuse_add(net, 1); err = tcp_set_ulp(sf->sk, "mptcp"); + +release_ssk: release_sock(sf->sk); if (err) { diff --git a/security/commoncap.c b/security/commoncap.c index 0b3fc2f3afe7..ab5742ab4362 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -314,7 +314,7 @@ int cap_inode_need_killpriv(struct dentry *dentry) * the vfsmount must be passed through @idmap. This function will then * take care to map the inode according to @idmap before checking * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs @nop_mnt_idmap. + * performed on the raw inode simply pass @nop_mnt_idmap. * * Return: 0 if successful, -ve on error. */ @@ -522,7 +522,7 @@ static bool validheader(size_t size, const struct vfs_cap_data *cap) * the vfsmount must be passed through @idmap. This function will then * take care to map the inode according to @idmap before checking * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs @nop_mnt_idmap. + * performed on the raw inode simply pass @nop_mnt_idmap. * * Return: On success, return the new size; on error, return < 0. */ @@ -630,7 +630,7 @@ static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps, * the vfsmount must be passed through @idmap. This function will then * take care to map the inode according to @idmap before checking * permissions. On non-idmapped mounts or if permission checking is to be - * performed on the raw inode simply passs @nop_mnt_idmap. + * performed on the raw inode simply pass @nop_mnt_idmap. */ int get_vfs_caps_from_disk(struct mnt_idmap *idmap, const struct dentry *dentry, @@ -1133,7 +1133,7 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) break; case LSM_SETID_FS: - /* juggle the capabilties to follow FSUID changes, unless + /* juggle the capabilities to follow FSUID changes, unless * otherwise suppressed * * FIXME - is fsuser used for all CAP_FS_MASK capabilities? @@ -1184,10 +1184,10 @@ static int cap_safe_nice(struct task_struct *p) } /** - * cap_task_setscheduler - Detemine if scheduler policy change is permitted + * cap_task_setscheduler - Determine if scheduler policy change is permitted * @p: The task to affect * - * Detemine if the requested scheduler policy change is permitted for the + * Determine if the requested scheduler policy change is permitted for the * specified task. * * Return: 0 if permission is granted, -ve if denied. @@ -1198,11 +1198,11 @@ int cap_task_setscheduler(struct task_struct *p) } /** - * cap_task_setioprio - Detemine if I/O priority change is permitted + * cap_task_setioprio - Determine if I/O priority change is permitted * @p: The task to affect * @ioprio: The I/O priority to set * - * Detemine if the requested I/O priority change is permitted for the specified + * Determine if the requested I/O priority change is permitted for the specified * task. * * Return: 0 if permission is granted, -ve if denied. @@ -1213,11 +1213,11 @@ int cap_task_setioprio(struct task_struct *p, int ioprio) } /** - * cap_task_setnice - Detemine if task priority change is permitted + * cap_task_setnice - Determine if task priority change is permitted * @p: The task to affect * @nice: The nice value to set * - * Detemine if the requested task priority change is permitted for the + * Determine if the requested task priority change is permitted for the * specified task. * * Return: 0 if permission is granted, -ve if denied. diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 7507d14eacc7..dc4df7475081 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -421,7 +421,7 @@ static bool verify_new_ex(struct dev_cgroup *dev_cgroup, } else { /* * new exception in the child will add more devices - * that can be acessed, so it can't match any of + * that can be accessed, so it can't match any of * parent's exceptions, even slightly */ match = match_exception_partial(&dev_cgroup->exceptions, @@ -822,7 +822,6 @@ struct cgroup_subsys devices_cgrp_subsys = { /** * devcgroup_legacy_check_permission - checks if an inode operation is permitted - * @dev_cgroup: the dev cgroup to be tested against * @type: device type * @major: device major number * @minor: device minor number diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 033804f5a5f2..0dae649f3740 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -40,7 +40,7 @@ static const char evm_hmac[] = "hmac(sha1)"; /** * evm_set_key() - set EVM HMAC key from the kernel * @key: pointer to a buffer with the key data - * @size: length of the key data + * @keylen: length of the key data * * This function allows setting the EVM HMAC key from the kernel * without using the "encrypted" key subsystem keys. It can be used diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cf24c5255583..c9b6e2a43478 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -318,7 +318,6 @@ int evm_protected_xattr_if_enabled(const char *req_xattr_name) /** * evm_read_protected_xattrs - read EVM protected xattr names, lengths, values * @dentry: dentry of the read xattrs - * @inode: inode of the read xattrs * @buffer: buffer xattr names, lengths or values are copied to * @buffer_size: size of buffer * @type: n: names, l: lengths, v: values @@ -390,6 +389,7 @@ int evm_read_protected_xattrs(struct dentry *dentry, u8 *buffer, * @xattr_name: requested xattr * @xattr_value: requested xattr value * @xattr_value_len: requested xattr value length + * @iint: inode integrity metadata * * Calculate the HMAC for the given dentry and verify it against the stored * security.evm xattr. For performance, use the xattr value and length @@ -795,7 +795,9 @@ static int evm_attr_change(struct mnt_idmap *idmap, /** * evm_inode_setattr - prevent updating an invalid EVM extended attribute + * @idmap: idmap of the mount * @dentry: pointer to the affected dentry + * @attr: iattr structure containing the new file attributes * * Permit update of file attributes when files have a valid EVM signature, * except in the case of them having an immutable portable signature. diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c73858e8c6d5..a462df827de2 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -43,12 +43,10 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) else if (inode > iint->inode) n = n->rb_right; else - break; + return iint; } - if (!n) - return NULL; - return iint; + return NULL; } /* @@ -113,10 +111,15 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) parent = *p; test_iint = rb_entry(parent, struct integrity_iint_cache, rb_node); - if (inode < test_iint->inode) + if (inode < test_iint->inode) { p = &(*p)->rb_left; - else + } else if (inode > test_iint->inode) { p = &(*p)->rb_right; + } else { + write_unlock(&integrity_iint_lock); + kmem_cache_free(iint_cache, iint); + return test_iint; + } } iint->inode = inode; diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index ce541b0ee1d3..452e80b541e5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include "ima.h" @@ -246,10 +245,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, struct inode *inode = file_inode(file); const char *filename = file->f_path.dentry->d_name.name; struct ima_max_digest_data hash; + struct kstat stat; int result = 0; int length; void *tmpbuf; - u64 i_version; + u64 i_version = 0; /* * Always collect the modsig, because IMA might have already collected @@ -268,7 +268,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, * to an initial measurement/appraisal/audit, but was modified to * assume the file changed. */ - i_version = inode_query_iversion(inode); + result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE, + AT_STATX_SYNC_AS_STAT); + if (!result && (stat.result_mask & STATX_CHANGE_COOKIE)) + i_version = stat.change_cookie; hash.hdr.algo = algo; hash.hdr.length = hash_digest_size[algo]; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index d66a0a36415e..365db0e43d7c 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include "ima.h" @@ -164,11 +163,16 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, mutex_lock(&iint->mutex); if (atomic_read(&inode->i_writecount) == 1) { + struct kstat stat; + update = test_and_clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags); - if (!IS_I_VERSION(inode) || - !inode_eq_iversion(inode, iint->version) || - (iint->flags & IMA_NEW_FILE)) { + if ((iint->flags & IMA_NEW_FILE) || + vfs_getattr_nosec(&file->f_path, &stat, + STATX_CHANGE_COOKIE, + AT_STATX_SYNC_AS_STAT) || + !(stat.result_mask & STATX_CHANGE_COOKIE) || + stat.change_cookie != iint->version) { iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->measured_pcrs = 0; if (update) diff --git a/security/integrity/ima/ima_modsig.c b/security/integrity/ima/ima_modsig.c index fb25723c65bc..3e7bee30080f 100644 --- a/security/integrity/ima/ima_modsig.c +++ b/security/integrity/ima/ima_modsig.c @@ -89,6 +89,9 @@ int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len, /** * ima_collect_modsig - Calculate the file hash without the appended signature. + * @modsig: parsed module signature + * @buf: data to verify the signature on + * @size: data size * * Since the modsig is part of the file contents, the hash used in its signature * isn't the same one ordinarily calculated by IMA. Therefore PKCS7 code diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3ca8b7348c2e..c9b3bd8f1bb9 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -721,6 +721,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * @secid: LSM secid of the task to be validated * @func: IMA hook identifier * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) + * @flags: IMA actions to consider (e.g. IMA_MEASURE | IMA_APPRAISE) * @pcr: set the pcr to extend * @template_desc: the template that should be used for this rule * @func_data: func specific data, may be NULL @@ -1915,7 +1916,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /** * ima_parse_add_rule - add a rule to ima_policy_rules - * @rule - ima measurement policy rule + * @rule: ima measurement policy rule * * Avoid locking by allowing just one writer at a time in ima_write_policy() * Returns the length of the rule parsed, an error code on failure diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig index 8e33c4e8ffb8..c1e862a38410 100644 --- a/security/landlock/Kconfig +++ b/security/landlock/Kconfig @@ -2,7 +2,7 @@ config SECURITY_LANDLOCK bool "Landlock support" - depends on SECURITY && !ARCH_EPHEMERAL_INODES + depends on SECURITY select SECURITY_PATH help Landlock is a sandboxing mechanism that enables processes to restrict diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 368e77ca43c4..849e832719e2 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -200,7 +200,7 @@ static void dump_common_audit_data(struct audit_buffer *ab, char comm[sizeof(current->comm)]; /* - * To keep stack sizes in check force programers to notice if they + * To keep stack sizes in check force programmers to notice if they * start making this union too large! See struct lsm_network_audit * as an example of how to deal with large data. */ diff --git a/security/safesetid/lsm.c b/security/safesetid/lsm.c index e806739f7868..5be5894aa0ea 100644 --- a/security/safesetid/lsm.c +++ b/security/safesetid/lsm.c @@ -131,7 +131,7 @@ static int safesetid_security_capable(const struct cred *cred, * set*gid() (e.g. setting up userns gid mappings). */ pr_warn("Operation requires CAP_SETGID, which is not available to GID %u for operations besides approved set*gid transitions\n", - __kuid_val(cred->uid)); + __kgid_val(cred->gid)); return -EPERM; default: /* Error, the only capabilities were checking for is CAP_SETUID/GID */ diff --git a/security/security.c b/security/security.c index d5ff7ff45b77..b720424ca37d 100644 --- a/security/security.c +++ b/security/security.c @@ -2491,7 +2491,7 @@ int security_inode_copy_up_xattr(const char *name) /* * The implementation can return 0 (accept the xattr), 1 (discard the * xattr), -EOPNOTSUPP if it does not know anything about the xattr or - * any other error code incase of an error. + * any other error code in case of an error. */ hlist_for_each_entry(hp, &security_hook_heads.inode_copy_up_xattr, list) { @@ -4667,6 +4667,23 @@ int security_sctp_assoc_established(struct sctp_association *asoc, } EXPORT_SYMBOL(security_sctp_assoc_established); +/** + * security_mptcp_add_subflow() - Inherit the LSM label from the MPTCP socket + * @sk: the owning MPTCP socket + * @ssk: the new subflow + * + * Update the labeling for the given MPTCP subflow, to match the one of the + * owning MPTCP socket. This hook has to be called after the socket creation and + * initialization via the security_socket_create() and + * security_socket_post_create() LSM hooks. + * + * Return: Returns 0 on success or a negative error code on failure. + */ +int security_mptcp_add_subflow(struct sock *sk, struct sock *ssk) +{ + return call_int_hook(mptcp_add_subflow, 0, sk, ssk); +} + #endif /* CONFIG_SECURITY_NETWORK */ #ifdef CONFIG_SECURITY_INFINIBAND @@ -4676,7 +4693,7 @@ EXPORT_SYMBOL(security_sctp_assoc_established); * @subnet_prefix: subnet prefix of the port * @pkey: IB pkey * - * Check permission to access a pkey when modifing a QP. + * Check permission to access a pkey when modifying a QP. * * Return: Returns 0 if permission is granted. */ diff --git a/security/selinux/Makefile b/security/selinux/Makefile index 8b21520bd4b9..836379639058 100644 --- a/security/selinux/Makefile +++ b/security/selinux/Makefile @@ -3,32 +3,38 @@ # Makefile for building the SELinux module as part of the kernel tree. # +# NOTE: There are a number of improvements that can be made to this Makefile +# once the kernel requires make v4.3 or greater; the most important feature +# lacking in older versions of make is support for grouped targets. These +# improvements are noted inline in the Makefile below ... + obj-$(CONFIG_SECURITY_SELINUX) := selinux.o +ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include + selinux-y := avc.o hooks.o selinuxfs.o netlink.o nlmsgtab.o netif.o \ netnode.o netport.o status.o \ ss/ebitmap.o ss/hashtab.o ss/symtab.o ss/sidtab.o ss/avtab.o \ ss/policydb.o ss/services.o ss/conditional.o ss/mls.o ss/context.o selinux-$(CONFIG_SECURITY_NETWORK_XFRM) += xfrm.o - selinux-$(CONFIG_NETLABEL) += netlabel.o - selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o - selinux-$(CONFIG_IMA) += ima.o -ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include +genhdrs := flask.h av_permissions.h +# see the note above, replace the dependency rule with the one below: +# $(addprefix $(obj)/,$(selinux-y)): $(addprefix $(obj)/,$(genhdrs)) $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h -quiet_cmd_flask = GEN $(obj)/flask.h $(obj)/av_permissions.h - cmd_flask = $< $(obj)/flask.h $(obj)/av_permissions.h +quiet_cmd_genhdrs = GEN $(addprefix $(obj)/,$(genhdrs)) + cmd_genhdrs = $< $(addprefix $(obj)/,$(genhdrs)) -targets += flask.h av_permissions.h -# once make >= 4.3 is required, we can use grouped targets in the rule below, -# which basically involves adding both headers and a '&' before the colon, see -# the example below: -# $(obj)/flask.h $(obj)/av_permissions.h &: scripts/selinux/... +# see the note above, replace the $targets and 'flask.h' rule with the lines +# below: +# targets += $(genhdrs) +# $(addprefix $(obj)/,$(genhdrs)) &: scripts/selinux/... +targets += flask.h $(obj)/flask.h: scripts/selinux/genheaders/genheaders FORCE - $(call if_changed,flask) + $(call if_changed,genhdrs) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 9367609b1e72..f6de0220b584 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -650,7 +650,6 @@ static void avc_insert(u32 ssid, u32 tsid, u16 tclass, trace_android_rvh_selinux_avc_insert(node); found: spin_unlock_irqrestore(lock, flag); - return; } /** @@ -1211,22 +1210,3 @@ u32 avc_policy_seqno(void) { return selinux_avc.avc_cache.latest_notif; } - -void avc_disable(void) -{ - /* - * If you are looking at this because you have realized that we are - * not destroying the avc_node_cachep it might be easy to fix, but - * I don't know the memory barrier semantics well enough to know. It's - * possible that some other task dereferenced security_ops when - * it still pointed to selinux operations. If that is the case it's - * possible that it is about to use the avc and is about to need the - * avc_node_cachep. I know I could wrap the security.c security_ops call - * in an rcu_lock, but seriously, it's not worth it. Instead I just flush - * the cache and get that memory back. - */ - if (avc_node_cachep) { - avc_flush(); - /* kmem_cache_destroy(avc_node_cachep); */ - } -} diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 79b4890e9936..d06e350fedee 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -357,7 +357,7 @@ enum { }; #define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg} -static struct { +static const struct { const char *name; int len; int opt; @@ -605,6 +605,13 @@ static int selinux_set_mnt_opts(struct super_block *sb, u32 defcontext_sid = 0; int rc = 0; + /* + * Specifying internal flags without providing a place to + * place the results is not allowed + */ + if (kern_flags && !set_kern_flags) + return -EINVAL; + mutex_lock(&sbsec->lock); if (!selinux_initialized()) { @@ -612,6 +619,10 @@ static int selinux_set_mnt_opts(struct super_block *sb, /* Defer initialization until selinux_complete_init, after the initial policy is loaded and the security server is ready to handle calls. */ + if (kern_flags & SECURITY_LSM_NATIVE_LABELS) { + sbsec->flags |= SE_SBNATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } goto out; } rc = -EINVAL; @@ -619,12 +630,6 @@ static int selinux_set_mnt_opts(struct super_block *sb, "before the security server is initialized\n"); goto out; } - if (kern_flags && !set_kern_flags) { - /* Specifying internal flags without providing a place to - * place the results is not allowed */ - rc = -EINVAL; - goto out; - } /* * Binary mount data FS will come through this function twice. Once @@ -757,7 +762,17 @@ static int selinux_set_mnt_opts(struct super_block *sb, * sets the label used on all file below the mountpoint, and will set * the superblock context if not already set. */ - if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { + if (sbsec->flags & SE_SBNATIVE) { + /* + * This means we are initializing a superblock that has been + * mounted before the SELinux was initialized and the + * filesystem requested native labeling. We had already + * returned SECURITY_LSM_NATIVE_LABELS in *set_kern_flags + * in the original mount attempt, so now we just need to set + * the SECURITY_FS_USE_NATIVE behavior. + */ + sbsec->behavior = SECURITY_FS_USE_NATIVE; + } else if (kern_flags & SECURITY_LSM_NATIVE_LABELS && !context_sid) { sbsec->behavior = SECURITY_FS_USE_NATIVE; *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; } @@ -868,13 +883,6 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, int set_context = (oldsbsec->flags & CONTEXT_MNT); int set_rootcontext = (oldsbsec->flags & ROOTCONTEXT_MNT); - /* - * if the parent was able to be mounted it clearly had no special lsm - * mount options. thus we can safely deal with this superblock later - */ - if (!selinux_initialized()) - return 0; - /* * Specifying internal flags without providing a place to * place the results is not allowed. @@ -882,18 +890,31 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb, if (kern_flags && !set_kern_flags) return -EINVAL; + mutex_lock(&newsbsec->lock); + + /* + * if the parent was able to be mounted it clearly had no special lsm + * mount options. thus we can safely deal with this superblock later + */ + if (!selinux_initialized()) { + if (kern_flags & SECURITY_LSM_NATIVE_LABELS) { + newsbsec->flags |= SE_SBNATIVE; + *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; + } + goto out; + } + /* how can we clone if the old one wasn't set up?? */ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED)); /* if fs is reusing a sb, make sure that the contexts match */ if (newsbsec->flags & SE_SBINITIALIZED) { + mutex_unlock(&newsbsec->lock); if ((kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context) *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS; return selinux_cmp_sb_context(oldsb, newsb); } - mutex_lock(&newsbsec->lock); - newsbsec->flags = oldsbsec->flags; newsbsec->sid = oldsbsec->sid; @@ -937,7 +958,7 @@ out: } /* - * NOTE: the caller is resposible for freeing the memory even if on error. + * NOTE: the caller is responsible for freeing the memory even if on error. */ static int selinux_add_opt(int token, const char *s, void **mnt_opts) { @@ -1394,8 +1415,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent spin_unlock(&isec->lock); switch (sbsec->behavior) { + /* + * In case of SECURITY_FS_USE_NATIVE we need to re-fetch the labels + * via xattr when called from delayed_superblock_init(). + */ case SECURITY_FS_USE_NATIVE: - break; case SECURITY_FS_USE_XATTR: if (!(inode->i_opflags & IOP_XATTR)) { sid = sbsec->def_sid; @@ -5379,6 +5403,21 @@ static void selinux_sctp_sk_clone(struct sctp_association *asoc, struct sock *sk selinux_netlbl_sctp_sk_clone(sk, newsk); } +static int selinux_mptcp_add_subflow(struct sock *sk, struct sock *ssk) +{ + struct sk_security_struct *ssksec = ssk->sk_security; + struct sk_security_struct *sksec = sk->sk_security; + + ssksec->sclass = sksec->sclass; + ssksec->sid = sksec->sid; + + /* replace the existing subflow label deleting the existing one + * and re-recreating a new label using the updated context + */ + selinux_netlbl_sk_security_free(ssksec); + return selinux_netlbl_socket_post_create(ssk, ssk->sk_family); +} + static int selinux_inet_conn_request(const struct sock *sk, struct sk_buff *skb, struct request_sock *req) { @@ -7074,6 +7113,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = { LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone), LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect), LSM_HOOK_INIT(sctp_assoc_established, selinux_sctp_assoc_established), + LSM_HOOK_INIT(mptcp_add_subflow, selinux_mptcp_add_subflow), LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request), LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone), LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established), diff --git a/security/selinux/ima.c b/security/selinux/ima.c index 7daf59667f59..aa34da9b0aeb 100644 --- a/security/selinux/ima.c +++ b/security/selinux/ima.c @@ -4,7 +4,7 @@ * * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com) * - * Measure critical data structures maintainted by SELinux + * Measure critical data structures maintained by SELinux * using IMA subsystem. */ #include diff --git a/security/selinux/include/audit.h b/security/selinux/include/audit.h index 406bceb90c6c..d5495134a5b9 100644 --- a/security/selinux/include/audit.h +++ b/security/selinux/include/audit.h @@ -41,7 +41,7 @@ void selinux_audit_rule_free(void *rule); * selinux_audit_rule_match - determine if a context ID matches a rule. * @sid: the context ID to check * @field: the field this rule refers to - * @op: the operater the rule uses + * @op: the operator the rule uses * @rule: pointer to the audit rule to check against * * Returns 1 if the context id matches the rule, 0 if it does not, and diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 9301222c8e55..9e055f74daf6 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -168,9 +168,6 @@ int avc_get_hash_stats(char *page); unsigned int avc_get_cache_threshold(void); void avc_set_cache_threshold(unsigned int cache_threshold); -/* Attempt to free avc node cache */ -void avc_disable(void); - #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats); #endif diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h index c992f83b0aae..875b055849e1 100644 --- a/security/selinux/include/ibpkey.h +++ b/security/selinux/include/ibpkey.h @@ -15,6 +15,7 @@ #define _SELINUX_IB_PKEY_H #include +#include "flask.h" #ifdef CONFIG_SECURITY_INFINIBAND void sel_ib_pkey_flush(void); diff --git a/security/selinux/include/ima.h b/security/selinux/include/ima.h index 05e04172c86d..93c05e97eb7f 100644 --- a/security/selinux/include/ima.h +++ b/security/selinux/include/ima.h @@ -4,7 +4,7 @@ * * Author: Lakshmi Ramasubramanian (nramas@linux.microsoft.com) * - * Measure critical data structures maintainted by SELinux + * Measure critical data structures maintained by SELinux * using IMA subsystem. */ diff --git a/security/selinux/include/initial_sid_to_string.h b/security/selinux/include/initial_sid_to_string.h index 60820517aa43..ecc6e74fa09b 100644 --- a/security/selinux/include/initial_sid_to_string.h +++ b/security/selinux/include/initial_sid_to_string.h @@ -1,4 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +#include + static const char *const initial_sid_to_string[] = { NULL, "kernel", diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ae4f9055566b..32daa4f86bc9 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -65,6 +65,7 @@ #define SE_SBPROC 0x0200 #define SE_SBGENFS 0x0400 #define SE_SBGENFS_XATTR 0x0800 +#define SE_SBNATIVE 0x1000 #define CONTEXT_STR "context" #define FSCONTEXT_STR "fscontext" @@ -400,7 +401,6 @@ struct selinux_kernel_status { extern void selinux_status_update_setenforce(int enforcing); extern void selinux_status_update_policyload(int seqno); extern void selinux_complete_init(void); -extern void exit_sel_fs(void); extern struct path selinux_null; extern void selnl_notify_setenforce(int val); extern void selnl_notify_policyload(u32 seqno); diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 767c670d33ea..528f5186e912 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -154,8 +154,12 @@ void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway) */ void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec) { - if (sksec->nlbl_secattr != NULL) - netlbl_secattr_free(sksec->nlbl_secattr); + if (!sksec->nlbl_secattr) + return; + + netlbl_secattr_free(sksec->nlbl_secattr); + sksec->nlbl_secattr = NULL; + sksec->nlbl_state = NLBL_UNSET; } /** diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 69a583b91fc5..bad1f6b685fd 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -951,7 +951,7 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) * either whitespace or multibyte characters, they shall be * encoded based on the percentage-encoding rule. * If not encoded, the sscanf logic picks up only left-half - * of the supplied name; splitted by a whitespace unexpectedly. + * of the supplied name; split by a whitespace unexpectedly. */ char *r, *w; int c1, c2; @@ -1649,7 +1649,7 @@ static int sel_make_ss_files(struct dentry *dir) struct super_block *sb = dir->d_sb; struct selinux_fs_info *fsi = sb->s_fs_info; int i; - static struct tree_descr files[] = { + static const struct tree_descr files[] = { { "sidtab_hash_stats", &sel_sidtab_hash_stats_ops, S_IRUGO }, }; diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 8480ec6c6e75..6766edc0fe68 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -354,7 +354,7 @@ int avtab_alloc_dup(struct avtab *new, const struct avtab *orig) return avtab_alloc_common(new, orig->nslot); } -void avtab_hash_eval(struct avtab *h, char *tag) +void avtab_hash_eval(struct avtab *h, const char *tag) { int i, chain_len, slots_used, max_chain_len; unsigned long long chain2_len_sum; diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index d3ebea8d146f..d6742fd9c560 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -92,7 +92,7 @@ int avtab_alloc(struct avtab *, u32); int avtab_alloc_dup(struct avtab *new, const struct avtab *orig); struct avtab_datum *avtab_search(struct avtab *h, const struct avtab_key *k); void avtab_destroy(struct avtab *h); -void avtab_hash_eval(struct avtab *h, char *tag); +void avtab_hash_eval(struct avtab *h, const char *tag); struct policydb; int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index e11219fdf9f7..b156c181c3c1 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -38,7 +38,7 @@ static int cond_evaluate_expr(struct policydb *p, struct cond_expr *expr) if (sp == (COND_EXPR_MAXDEPTH - 1)) return -1; sp++; - s[sp] = p->bool_val_to_struct[node->bool - 1]->state; + s[sp] = p->bool_val_to_struct[node->boolean - 1]->state; break; case COND_NOT: if (sp < 0) @@ -366,7 +366,7 @@ static int expr_node_isvalid(struct policydb *p, struct cond_expr_node *expr) return 0; } - if (expr->bool > p->p_bools.nprim) { + if (expr->boolean > p->p_bools.nprim) { pr_err("SELinux: conditional expressions uses unknown bool.\n"); return 0; } @@ -401,7 +401,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) return rc; expr->expr_type = le32_to_cpu(buf[0]); - expr->bool = le32_to_cpu(buf[1]); + expr->boolean = le32_to_cpu(buf[1]); if (!expr_node_isvalid(p, expr)) return -EINVAL; @@ -518,7 +518,7 @@ static int cond_write_node(struct policydb *p, struct cond_node *node, for (i = 0; i < node->expr.len; i++) { buf[0] = cpu_to_le32(node->expr.nodes[i].expr_type); - buf[1] = cpu_to_le32(node->expr.nodes[i].bool); + buf[1] = cpu_to_le32(node->expr.nodes[i].boolean); rc = put_entry(buf, sizeof(u32), 2, fp); if (rc) return rc; diff --git a/security/selinux/ss/conditional.h b/security/selinux/ss/conditional.h index e47ec6ddeaf6..5a7b51278dc6 100644 --- a/security/selinux/ss/conditional.h +++ b/security/selinux/ss/conditional.h @@ -29,7 +29,7 @@ struct cond_expr_node { #define COND_NEQ 7 /* bool != bool */ #define COND_LAST COND_NEQ u32 expr_type; - u32 bool; + u32 boolean; }; struct cond_expr { diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index eda32c3d4c0a..aed704b8c642 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -167,6 +167,8 @@ static inline int context_cpy(struct context *dst, const struct context *src) rc = mls_context_cpy(dst, src); if (rc) { kfree(dst->str); + dst->str = NULL; + dst->len = 0; return rc; } return 0; diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index a36825a046d9..d770622226d1 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -42,7 +42,7 @@ #include "services.h" #ifdef DEBUG_HASHES -static const char *symtab_name[SYM_NUM] = { +static const char *const symtab_name[SYM_NUM] = { "common prefixes", "classes", "roles", @@ -2257,6 +2257,10 @@ static int ocontext_read(struct policydb *p, const struct policydb_compat_info * if (rc) goto out; + if (i == OCON_FS) + pr_warn("SELinux: void and deprecated fs ocon %s\n", + c->u.name); + rc = context_read_and_validate(&c->context[0], p, fp); if (rc) goto out; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 1d758118e224..6c7e2ac94577 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -225,7 +225,7 @@ struct genfs { /* object context array indices */ #define OCON_ISID 0 /* initial SIDs */ -#define OCON_FS 1 /* unlabeled file systems */ +#define OCON_FS 1 /* unlabeled file systems (deprecated) */ #define OCON_PORT 2 /* TCP and UDP port numbers */ #define OCON_NETIF 3 /* network interfaces */ #define OCON_NODE 4 /* nodes */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index c5ef4007aa00..66829f04b690 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -585,7 +585,7 @@ static void type_attribute_bounds_av(struct policydb *policydb, /* * flag which drivers have permissions - * only looking for ioctl based extended permssions + * only looking for ioctl based extended permissions */ void services_compute_xperms_drivers( struct extended_perms *xperms, @@ -3548,38 +3548,38 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) tmprule = kzalloc(sizeof(struct selinux_audit_rule), GFP_KERNEL); if (!tmprule) return -ENOMEM; - context_init(&tmprule->au_ctxt); rcu_read_lock(); policy = rcu_dereference(state->policy); policydb = &policy->policydb; - tmprule->au_seqno = policy->latest_granting; - switch (field) { case AUDIT_SUBJ_USER: case AUDIT_OBJ_USER: - rc = -EINVAL; userdatum = symtab_search(&policydb->p_users, rulestr); - if (!userdatum) - goto out; + if (!userdatum) { + rc = -EINVAL; + goto err; + } tmprule->au_ctxt.user = userdatum->value; break; case AUDIT_SUBJ_ROLE: case AUDIT_OBJ_ROLE: - rc = -EINVAL; roledatum = symtab_search(&policydb->p_roles, rulestr); - if (!roledatum) - goto out; + if (!roledatum) { + rc = -EINVAL; + goto err; + } tmprule->au_ctxt.role = roledatum->value; break; case AUDIT_SUBJ_TYPE: case AUDIT_OBJ_TYPE: - rc = -EINVAL; typedatum = symtab_search(&policydb->p_types, rulestr); - if (!typedatum) - goto out; + if (!typedatum) { + rc = -EINVAL; + goto err; + } tmprule->au_ctxt.type = typedatum->value; break; case AUDIT_SUBJ_SEN: @@ -3589,20 +3589,18 @@ int selinux_audit_rule_init(u32 field, u32 op, char *rulestr, void **vrule) rc = mls_from_string(policydb, rulestr, &tmprule->au_ctxt, GFP_ATOMIC); if (rc) - goto out; + goto err; break; } - rc = 0; -out: rcu_read_unlock(); - if (rc) { - selinux_audit_rule_free(tmprule); - tmprule = NULL; - } - *rule = tmprule; + return 0; +err: + rcu_read_unlock(); + selinux_audit_rule_free(tmprule); + *rule = NULL; return rc; } diff --git a/security/smack/smack.h b/security/smack/smack.h index e2239be7bd60..aa15ff56ed6e 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -120,6 +120,7 @@ struct inode_smack { struct task_smack { struct smack_known *smk_task; /* label for access control */ struct smack_known *smk_forked; /* label when forked */ + struct smack_known *smk_transmuted;/* label when transmuted */ struct list_head smk_rules; /* per task access rules */ struct mutex smk_rules_lock; /* lock for the rules */ struct list_head smk_relabel; /* transit allowed labels */ diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 7a3e9ab137d8..6e270cf3fd30 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -933,8 +933,9 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, const char **name, void **value, size_t *len) { + struct task_smack *tsp = smack_cred(current_cred()); struct inode_smack *issp = smack_inode(inode); - struct smack_known *skp = smk_of_current(); + struct smack_known *skp = smk_of_task(tsp); struct smack_known *isp = smk_of_inode(inode); struct smack_known *dsp = smk_of_inode(dir); int may; @@ -943,20 +944,34 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, *name = XATTR_SMACK_SUFFIX; if (value && len) { - rcu_read_lock(); - may = smk_access_entry(skp->smk_known, dsp->smk_known, - &skp->smk_rules); - rcu_read_unlock(); + /* + * If equal, transmuting already occurred in + * smack_dentry_create_files_as(). No need to check again. + */ + if (tsp->smk_task != tsp->smk_transmuted) { + rcu_read_lock(); + may = smk_access_entry(skp->smk_known, dsp->smk_known, + &skp->smk_rules); + rcu_read_unlock(); + } /* - * If the access rule allows transmutation and - * the directory requests transmutation then - * by all means transmute. + * In addition to having smk_task equal to smk_transmuted, + * if the access rule allows transmutation and the directory + * requests transmutation then by all means transmute. * Mark the inode as changed. */ - if (may > 0 && ((may & MAY_TRANSMUTE) != 0) && - smk_inode_transmutable(dir)) { - isp = dsp; + if ((tsp->smk_task == tsp->smk_transmuted) || + (may > 0 && ((may & MAY_TRANSMUTE) != 0) && + smk_inode_transmutable(dir))) { + /* + * The caller of smack_dentry_create_files_as() + * should have overridden the current cred, so the + * inode label was already set correctly in + * smack_inode_alloc_security(). + */ + if (tsp->smk_task != tsp->smk_transmuted) + isp = dsp; issp->smk_flags |= SMK_INODE_CHANGED; } @@ -1463,10 +1478,19 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap, struct super_block *sbp; struct inode *ip = inode; struct smack_known *isp; + struct inode_smack *ispp; + size_t label_len; + char *label = NULL; - if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) + if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) { isp = smk_of_inode(inode); - else { + } else if (strcmp(name, XATTR_SMACK_TRANSMUTE) == 0) { + ispp = smack_inode(inode); + if (ispp->smk_flags & SMK_INODE_TRANSMUTE) + label = TRANS_TRUE; + else + label = ""; + } else { /* * The rest of the Smack xattrs are only on sockets. */ @@ -1488,13 +1512,18 @@ static int smack_inode_getsecurity(struct mnt_idmap *idmap, return -EOPNOTSUPP; } + if (!label) + label = isp->smk_known; + + label_len = strlen(label); + if (alloc) { - *buffer = kstrdup(isp->smk_known, GFP_KERNEL); + *buffer = kstrdup(label, GFP_KERNEL); if (*buffer == NULL) return -ENOMEM; } - return strlen(isp->smk_known); + return label_len; } @@ -4753,8 +4782,10 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode, * providing access is transmuting use the containing * directory label instead of the process label. */ - if (may > 0 && (may & MAY_TRANSMUTE)) + if (may > 0 && (may & MAY_TRANSMUTE)) { ntsp->smk_task = isp->smk_inode; + ntsp->smk_transmuted = ntsp->smk_task; + } } return 0; } diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config index 0f0a65287bac..3dc9e438eab1 100644 --- a/tools/testing/selftests/landlock/config +++ b/tools/testing/selftests/landlock/config @@ -1,7 +1,10 @@ +CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y CONFIG_OVERLAY_FS=y -CONFIG_SECURITY_LANDLOCK=y -CONFIG_SECURITY_PATH=y +CONFIG_PROC_FS=y CONFIG_SECURITY=y +CONFIG_SECURITY_LANDLOCK=y CONFIG_SHMEM=y -CONFIG_TMPFS_XATTR=y +CONFIG_SYSFS=y CONFIG_TMPFS=y +CONFIG_TMPFS_XATTR=y diff --git a/tools/testing/selftests/landlock/config.um b/tools/testing/selftests/landlock/config.um new file mode 100644 index 000000000000..40937c0395d6 --- /dev/null +++ b/tools/testing/selftests/landlock/config.um @@ -0,0 +1 @@ +CONFIG_HOSTFS=y diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c index b6c4be3faf7a..83d565569512 100644 --- a/tools/testing/selftests/landlock/fs_test.c +++ b/tools/testing/selftests/landlock/fs_test.c @@ -10,6 +10,7 @@ #define _GNU_SOURCE #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include "common.h" @@ -107,8 +109,10 @@ static bool fgrep(FILE *const inf, const char *const str) return false; } -static bool supports_overlayfs(void) +static bool supports_filesystem(const char *const filesystem) { + char str[32]; + int len; bool res; FILE *const inf = fopen("/proc/filesystems", "r"); @@ -119,11 +123,33 @@ static bool supports_overlayfs(void) if (!inf) return true; - res = fgrep(inf, "nodev\toverlay\n"); + /* filesystem can be null for bind mounts. */ + if (!filesystem) + return true; + + len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem); + if (len >= sizeof(str)) + /* Ignores too-long filesystem names. */ + return true; + + res = fgrep(inf, str); fclose(inf); return res; } +static bool cwd_matches_fs(unsigned int fs_magic) +{ + struct statfs statfs_buf; + + if (!fs_magic) + return true; + + if (statfs(".", &statfs_buf)) + return true; + + return statfs_buf.f_type == fs_magic; +} + static void mkdir_parents(struct __test_metadata *const _metadata, const char *const path) { @@ -206,7 +232,26 @@ out: return err; } -static void prepare_layout(struct __test_metadata *const _metadata) +struct mnt_opt { + const char *const source; + const char *const type; + const unsigned long flags; + const char *const data; +}; + +const struct mnt_opt mnt_tmp = { + .type = "tmpfs", + .data = "size=4m,mode=700", +}; + +static int mount_opt(const struct mnt_opt *const mnt, const char *const target) +{ + return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags, + mnt->data); +} + +static void prepare_layout_opt(struct __test_metadata *const _metadata, + const struct mnt_opt *const mnt) { disable_caps(_metadata); umask(0077); @@ -217,12 +262,28 @@ static void prepare_layout(struct __test_metadata *const _metadata) * for tests relying on pivot_root(2) and move_mount(2). */ set_cap(_metadata, CAP_SYS_ADMIN); - ASSERT_EQ(0, unshare(CLONE_NEWNS)); - ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700")); + ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP)); + ASSERT_EQ(0, mount_opt(mnt, TMP_DIR)) + { + TH_LOG("Failed to mount the %s filesystem: %s", mnt->type, + strerror(errno)); + /* + * FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP() + * failed, so we need to explicitly do a minimal cleanup to + * avoid cascading errors with other tests that don't depend on + * the same filesystem. + */ + remove_path(TMP_DIR); + } ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL)); clear_cap(_metadata, CAP_SYS_ADMIN); } +static void prepare_layout(struct __test_metadata *const _metadata) +{ + prepare_layout_opt(_metadata, &mnt_tmp); +} + static void cleanup_layout(struct __test_metadata *const _metadata) { set_cap(_metadata, CAP_SYS_ADMIN); @@ -231,6 +292,20 @@ static void cleanup_layout(struct __test_metadata *const _metadata) EXPECT_EQ(0, remove_path(TMP_DIR)); } +/* clang-format off */ +FIXTURE(layout0) {}; +/* clang-format on */ + +FIXTURE_SETUP(layout0) +{ + prepare_layout(_metadata); +} + +FIXTURE_TEARDOWN(layout0) +{ + cleanup_layout(_metadata); +} + static void create_layout1(struct __test_metadata *const _metadata) { create_file(_metadata, file1_s1d1); @@ -248,7 +323,7 @@ static void create_layout1(struct __test_metadata *const _metadata) create_file(_metadata, file1_s3d1); create_directory(_metadata, dir_s3d2); set_cap(_metadata, CAP_SYS_ADMIN); - ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700")); + ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2)); clear_cap(_metadata, CAP_SYS_ADMIN); ASSERT_EQ(0, mkdir(dir_s3d3, 0700)); @@ -262,11 +337,13 @@ static void remove_layout1(struct __test_metadata *const _metadata) EXPECT_EQ(0, remove_path(file1_s1d3)); EXPECT_EQ(0, remove_path(file1_s1d2)); EXPECT_EQ(0, remove_path(file1_s1d1)); + EXPECT_EQ(0, remove_path(dir_s1d3)); EXPECT_EQ(0, remove_path(file2_s2d3)); EXPECT_EQ(0, remove_path(file1_s2d3)); EXPECT_EQ(0, remove_path(file1_s2d2)); EXPECT_EQ(0, remove_path(file1_s2d1)); + EXPECT_EQ(0, remove_path(dir_s2d2)); EXPECT_EQ(0, remove_path(file1_s3d1)); EXPECT_EQ(0, remove_path(dir_s3d3)); @@ -510,7 +587,7 @@ TEST_F_FORK(layout1, file_and_dir_access_rights) ASSERT_EQ(0, close(ruleset_fd)); } -TEST_F_FORK(layout1, unknown_access_rights) +TEST_F_FORK(layout0, unknown_access_rights) { __u64 access_mask; @@ -608,7 +685,7 @@ static void enforce_ruleset(struct __test_metadata *const _metadata, } } -TEST_F_FORK(layout1, proc_nsfs) +TEST_F_FORK(layout0, proc_nsfs) { const struct rule rules[] = { { @@ -657,11 +734,11 @@ TEST_F_FORK(layout1, proc_nsfs) ASSERT_EQ(0, close(path_beneath.parent_fd)); } -TEST_F_FORK(layout1, unpriv) +TEST_F_FORK(layout0, unpriv) { const struct rule rules[] = { { - .path = dir_s1d2, + .path = TMP_DIR, .access = ACCESS_RO, }, {}, @@ -1301,12 +1378,12 @@ TEST_F_FORK(layout1, inherit_superset) ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY)); } -TEST_F_FORK(layout1, max_layers) +TEST_F_FORK(layout0, max_layers) { int i, err; const struct rule rules[] = { { - .path = dir_s1d2, + .path = TMP_DIR, .access = ACCESS_RO, }, {}, @@ -4030,21 +4107,24 @@ static const char (*merge_sub_files[])[] = { * └── work */ -/* clang-format off */ -FIXTURE(layout2_overlay) {}; -/* clang-format on */ +FIXTURE(layout2_overlay) +{ + bool skip_test; +}; FIXTURE_SETUP(layout2_overlay) { - if (!supports_overlayfs()) - SKIP(return, "overlayfs is not supported"); + if (!supports_filesystem("overlay")) { + self->skip_test = true; + SKIP(return, "overlayfs is not supported (setup)"); + } prepare_layout(_metadata); create_directory(_metadata, LOWER_BASE); set_cap(_metadata, CAP_SYS_ADMIN); /* Creates tmpfs mount points to get deterministic overlayfs. */ - ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700")); + ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE)); clear_cap(_metadata, CAP_SYS_ADMIN); create_file(_metadata, lower_fl1); create_file(_metadata, lower_dl1_fl2); @@ -4054,7 +4134,7 @@ FIXTURE_SETUP(layout2_overlay) create_directory(_metadata, UPPER_BASE); set_cap(_metadata, CAP_SYS_ADMIN); - ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700")); + ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE)); clear_cap(_metadata, CAP_SYS_ADMIN); create_file(_metadata, upper_fu1); create_file(_metadata, upper_du1_fu2); @@ -4075,8 +4155,8 @@ FIXTURE_SETUP(layout2_overlay) FIXTURE_TEARDOWN(layout2_overlay) { - if (!supports_overlayfs()) - SKIP(return, "overlayfs is not supported"); + if (self->skip_test) + SKIP(return, "overlayfs is not supported (teardown)"); EXPECT_EQ(0, remove_path(lower_do1_fl3)); EXPECT_EQ(0, remove_path(lower_dl1_fl2)); @@ -4109,8 +4189,8 @@ FIXTURE_TEARDOWN(layout2_overlay) TEST_F_FORK(layout2_overlay, no_restriction) { - if (!supports_overlayfs()) - SKIP(return, "overlayfs is not supported"); + if (self->skip_test) + SKIP(return, "overlayfs is not supported (test)"); ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY)); ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY)); @@ -4275,8 +4355,8 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) size_t i; const char *path_entry; - if (!supports_overlayfs()) - SKIP(return, "overlayfs is not supported"); + if (self->skip_test) + SKIP(return, "overlayfs is not supported (test)"); /* Sets rules on base directories (i.e. outside overlay scope). */ ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base); @@ -4423,4 +4503,261 @@ TEST_F_FORK(layout2_overlay, same_content_different_file) } } +FIXTURE(layout3_fs) +{ + bool has_created_dir; + bool has_created_file; + char *dir_path; + bool skip_test; +}; + +FIXTURE_VARIANT(layout3_fs) +{ + const struct mnt_opt mnt; + const char *const file_path; + unsigned int cwd_fs_magic; +}; + +/* clang-format off */ +FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) { + /* clang-format on */ + .mnt = mnt_tmp, + .file_path = file1_s1d1, +}; + +FIXTURE_VARIANT_ADD(layout3_fs, ramfs) { + .mnt = { + .type = "ramfs", + .data = "mode=700", + }, + .file_path = TMP_DIR "/dir/file", +}; + +FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) { + .mnt = { + .type = "cgroup2", + }, + .file_path = TMP_DIR "/test/cgroup.procs", +}; + +FIXTURE_VARIANT_ADD(layout3_fs, proc) { + .mnt = { + .type = "proc", + }, + .file_path = TMP_DIR "/self/status", +}; + +FIXTURE_VARIANT_ADD(layout3_fs, sysfs) { + .mnt = { + .type = "sysfs", + }, + .file_path = TMP_DIR "/kernel/notes", +}; + +FIXTURE_VARIANT_ADD(layout3_fs, hostfs) { + .mnt = { + .source = TMP_DIR, + .flags = MS_BIND, + }, + .file_path = TMP_DIR "/dir/file", + .cwd_fs_magic = HOSTFS_SUPER_MAGIC, +}; + +FIXTURE_SETUP(layout3_fs) +{ + struct stat statbuf; + const char *slash; + size_t dir_len; + + if (!supports_filesystem(variant->mnt.type) || + !cwd_matches_fs(variant->cwd_fs_magic)) { + self->skip_test = true; + SKIP(return, "this filesystem is not supported (setup)"); + } + + slash = strrchr(variant->file_path, '/'); + ASSERT_NE(slash, NULL); + dir_len = (size_t)slash - (size_t)variant->file_path; + ASSERT_LT(0, dir_len); + self->dir_path = malloc(dir_len + 1); + self->dir_path[dir_len] = '\0'; + strncpy(self->dir_path, variant->file_path, dir_len); + + prepare_layout_opt(_metadata, &variant->mnt); + + /* Creates directory when required. */ + if (stat(self->dir_path, &statbuf)) { + set_cap(_metadata, CAP_DAC_OVERRIDE); + EXPECT_EQ(0, mkdir(self->dir_path, 0700)) + { + TH_LOG("Failed to create directory \"%s\": %s", + self->dir_path, strerror(errno)); + free(self->dir_path); + self->dir_path = NULL; + } + self->has_created_dir = true; + clear_cap(_metadata, CAP_DAC_OVERRIDE); + } + + /* Creates file when required. */ + if (stat(variant->file_path, &statbuf)) { + int fd; + + set_cap(_metadata, CAP_DAC_OVERRIDE); + fd = creat(variant->file_path, 0600); + EXPECT_LE(0, fd) + { + TH_LOG("Failed to create file \"%s\": %s", + variant->file_path, strerror(errno)); + } + EXPECT_EQ(0, close(fd)); + self->has_created_file = true; + clear_cap(_metadata, CAP_DAC_OVERRIDE); + } +} + +FIXTURE_TEARDOWN(layout3_fs) +{ + if (self->skip_test) + SKIP(return, "this filesystem is not supported (teardown)"); + + if (self->has_created_file) { + set_cap(_metadata, CAP_DAC_OVERRIDE); + /* + * Don't check for error because the file might already + * have been removed (cf. release_inode test). + */ + unlink(variant->file_path); + clear_cap(_metadata, CAP_DAC_OVERRIDE); + } + + if (self->has_created_dir) { + set_cap(_metadata, CAP_DAC_OVERRIDE); + /* + * Don't check for error because the directory might already + * have been removed (cf. release_inode test). + */ + rmdir(self->dir_path); + clear_cap(_metadata, CAP_DAC_OVERRIDE); + } + free(self->dir_path); + self->dir_path = NULL; + + cleanup_layout(_metadata); +} + +static void layer3_fs_tag_inode(struct __test_metadata *const _metadata, + FIXTURE_DATA(layout3_fs) * self, + const FIXTURE_VARIANT(layout3_fs) * variant, + const char *const rule_path) +{ + const struct rule layer1_allow_read_file[] = { + { + .path = rule_path, + .access = LANDLOCK_ACCESS_FS_READ_FILE, + }, + {}, + }; + const struct landlock_ruleset_attr layer2_deny_everything_attr = { + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, + }; + const char *const dev_null_path = "/dev/null"; + int ruleset_fd; + + if (self->skip_test) + SKIP(return, "this filesystem is not supported (test)"); + + /* Checks without Landlock. */ + EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); + EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); + + ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE, + layer1_allow_read_file); + EXPECT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); + EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); + + /* Forbids directory reading. */ + ruleset_fd = + landlock_create_ruleset(&layer2_deny_everything_attr, + sizeof(layer2_deny_everything_attr), 0); + EXPECT_LE(0, ruleset_fd); + enforce_ruleset(_metadata, ruleset_fd); + EXPECT_EQ(0, close(ruleset_fd)); + + /* Checks with Landlock and forbidden access. */ + EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC)); + EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC)); +} + +/* Matrix of tests to check file hierarchy evaluation. */ + +TEST_F_FORK(layout3_fs, tag_inode_dir_parent) +{ + /* The current directory must not be the root for this test. */ + layer3_fs_tag_inode(_metadata, self, variant, "."); +} + +TEST_F_FORK(layout3_fs, tag_inode_dir_mnt) +{ + layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR); +} + +TEST_F_FORK(layout3_fs, tag_inode_dir_child) +{ + layer3_fs_tag_inode(_metadata, self, variant, self->dir_path); +} + +TEST_F_FORK(layout3_fs, tag_inode_file) +{ + layer3_fs_tag_inode(_metadata, self, variant, variant->file_path); +} + +/* Light version of layout1.release_inodes */ +TEST_F_FORK(layout3_fs, release_inodes) +{ + const struct rule layer1[] = { + { + .path = TMP_DIR, + .access = LANDLOCK_ACCESS_FS_READ_DIR, + }, + {}, + }; + int ruleset_fd; + + if (self->skip_test) + SKIP(return, "this filesystem is not supported (test)"); + + /* Clean up for the teardown to not fail. */ + if (self->has_created_file) + EXPECT_EQ(0, remove_path(variant->file_path)); + + if (self->has_created_dir) + /* Don't check for error because of cgroup specificities. */ + remove_path(self->dir_path); + + ruleset_fd = + create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1); + ASSERT_LE(0, ruleset_fd); + + /* Unmount the filesystem while it is being used by a ruleset. */ + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, umount(TMP_DIR)); + clear_cap(_metadata, CAP_SYS_ADMIN); + + /* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */ + set_cap(_metadata, CAP_SYS_ADMIN); + ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR)); + clear_cap(_metadata, CAP_SYS_ADMIN); + + enforce_ruleset(_metadata, ruleset_fd); + ASSERT_EQ(0, close(ruleset_fd)); + + /* Checks that access to the new mount point is denied. */ + ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY)); +} + TEST_HARNESS_MAIN