[PATCH RESEND v2 00/19] Support fuse mounts in user namespaces
These patches implement support for mounting filesystems in user namespaces using fuse. They are based on the patches in the for-testing branch of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace.git, but I've rebased them onto 4.4-rc3. I've pushed all of this to: git://git.kernel.org/pub/scm/linux/kernel/git/sforshee/linux.git fuse-userns The patches are organized into three high-level groups. Patches 1-6 are related to security, adding restrictions for unprivileged mounts and updating the LSMs as needed. Patches 1-2 (checking inode permissions for block device mounts) may not be strictly necessary for fuseblk mounts since fuse doesn't do any IO on the block device in the kernel, but it still seems like a good idea to fail the mount if the user doesn't have the required permissions for the inode (though this is a bit misleading with fuse since the mounts are done via a suid-root helper). Patches 7-14 update most of the vfs to translate ids correctly and deal with inodes which may have invalid user/group ids. I've omitted patches for anything not used by fuse - quota, fs freezing, some helper functions, etc. - but if these are wanted for the sake of completeness I can include them. Patches 15-18 update fuse to deal with mounts from non-init pid and user namespaces and enable mounting from user namespaces. Changes since v1: - Drop patch for FIBMAP. - Use current_in_userns in fuse_allow_current_process. - Remove checks for uid/gid validity in fuse. Intead, ids from the backing store which do not map into s_user_ns will result in invalid ids in the vfs inode. Checks in the vfs will prevent unmappable ids from being passed in from above. - Update a couple of commit messages to provide more detail about changes. Thanks, Seth Andy Lutomirski (1): fs: Treat foreign mounts as nosuid Seth Forshee (17): block_dev: Support checking inode permissions in lookup_bdev() block_dev: Check permissions towards block device inode when mounting selinux: Add support for unprivileged mounts from user namespaces userns: Replace in_userns with current_in_userns Smack: Handle labels consistently in untrusted mounts fs: Check for invalid i_uid in may_follow_link() cred: Reject inodes with invalid ids in set_create_file_as() fs: Refuse uid/gid changes which don't map into s_user_ns fs: Update posix_acl support to handle user namespace mounts fs: Ensure the mounter of a filesystem is privileged towards its inodes fs: Don't remove suid for CAP_FSETID in s_user_ns fs: Allow superblock owner to access do_remount_sb() capabilities: Allow privileged user in s_user_ns to set security.* xattrs fuse: Add support for pid namespaces fuse: Support fuse filesystems outside of init_user_ns fuse: Restrict allow_other to the superblock's namespace or a descendant fuse: Allow user namespace mounts drivers/md/bcache/super.c | 2 +- drivers/md/dm-table.c | 2 +- drivers/mtd/mtdsuper.c | 2 +- fs/attr.c | 11 +++ fs/block_dev.c | 18 +-- fs/exec.c | 2 +- fs/fuse/cuse.c | 3 +- fs/fuse/dev.c | 26 fs/fuse/dir.c | 16 +- fs/fuse/file.c | 22 +++--- fs/fuse/fuse_i.h| 10 +- fs/fuse/inode.c | 42 +- fs/inode.c | 6 +++- fs/namei.c | 2 +- fs/namespace.c | 17 +-- fs/posix_acl.c | 67 ++--- fs/quota/quota.c| 2 +- fs/xattr.c | 19 +--- include/linux/fs.h | 2 +- include/linux/mount.h | 1 + include/linux/posix_acl_xattr.h | 17 --- include/linux/uidgid.h | 10 ++ include/linux/user_namespace.h | 6 ++-- kernel/capability.c | 13 +--- kernel/cred.c | 2 ++ kernel/user_namespace.c | 6 ++-- security/commoncap.c| 16 ++ security/selinux/hooks.c| 25 ++- security/smack/smack_lsm.c | 29 -- 29 files changed, 287 insertions(+), 109 deletions(-) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 07/18] fs: Check for invalid i_uid in may_follow_link()
Filesystem uids which don't map into a user namespace may result in inode->i_uid being INVALID_UID. A symlink and its parent could have different owners in the filesystem can both get mapped to INVALID_UID, which may result in following a symlink when this would not have otherwise been permitted when protected symlinks are enabled. Add a new helper function, uid_valid_eq(), and use this to validate that the ids in may_follow_link() are both equal and valid. Also add an equivalent helper for gids, which is currently unused. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/namei.c | 2 +- include/linux/uidgid.h | 10 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/namei.c b/fs/namei.c index 288e8a74bf88..4ccafd391697 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -902,7 +902,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; /* Allowed if parent directory and link owner match. */ - if (uid_eq(parent->i_uid, inode->i_uid)) + if (uid_valid_eq(parent->i_uid, inode->i_uid)) return 0; if (nd->flags & LOOKUP_RCU) diff --git a/include/linux/uidgid.h b/include/linux/uidgid.h index 03835522dfcb..e09529fe2668 100644 --- a/include/linux/uidgid.h +++ b/include/linux/uidgid.h @@ -117,6 +117,16 @@ static inline bool gid_valid(kgid_t gid) return __kgid_val(gid) != (gid_t) -1; } +static inline bool uid_valid_eq(kuid_t left, kuid_t right) +{ + return uid_eq(left, right) && uid_valid(left); +} + +static inline bool gid_valid_eq(kgid_t left, kgid_t right) +{ + return gid_eq(left, right) && gid_valid(left); +} + #ifdef CONFIG_USER_NS extern kuid_t make_kuid(struct user_namespace *from, uid_t uid); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 05/18] userns: Replace in_userns with current_in_userns
All current callers of in_userns pass current_user_ns as the first argument. Simplify by replacing in_userns with current_in_userns which checks whether current_user_ns is in the namespace supplied as an argument. Signed-off-by: Seth Forshee Acked-by: James Morris Acked-by: Serge Hallyn --- fs/namespace.c | 2 +- include/linux/user_namespace.h | 6 ++ kernel/user_namespace.c| 6 +++--- security/commoncap.c | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 2101ce7b96ab..18fc58760aec 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3286,7 +3286,7 @@ bool mnt_may_suid(struct vfsmount *mnt) * in other namespaces. */ return !(mnt->mnt_flags & MNT_NOSUID) && check_mnt(real_mount(mnt)) && - in_userns(current_user_ns(), mnt->mnt_sb->s_user_ns); + current_in_userns(mnt->mnt_sb->s_user_ns); } static struct ns_common *mntns_get(struct task_struct *task) diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index a43faa727124..9217169c64cb 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -72,8 +72,7 @@ extern ssize_t proc_projid_map_write(struct file *, const char __user *, size_t, extern ssize_t proc_setgroups_write(struct file *, const char __user *, size_t, loff_t *); extern int proc_setgroups_show(struct seq_file *m, void *v); extern bool userns_may_setgroups(const struct user_namespace *ns); -extern bool in_userns(const struct user_namespace *ns, - const struct user_namespace *target_ns); +extern bool current_in_userns(const struct user_namespace *target_ns); #else static inline struct user_namespace *get_user_ns(struct user_namespace *ns) @@ -103,8 +102,7 @@ static inline bool userns_may_setgroups(const struct user_namespace *ns) return true; } -static inline bool in_userns(const struct user_namespace *ns, -const struct user_namespace *target_ns) +static inline bool current_in_userns(const struct user_namespace *target_ns) { return true; } diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index 69fbc377357b..5960edc7e644 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -949,10 +949,10 @@ bool userns_may_setgroups(const struct user_namespace *ns) * Returns true if @ns is the same namespace as or a descendant of * @target_ns. */ -bool in_userns(const struct user_namespace *ns, - const struct user_namespace *target_ns) +bool current_in_userns(const struct user_namespace *target_ns) { - for (; ns; ns = ns->parent) { + struct user_namespace *ns; + for (ns = current_user_ns(); ns; ns = ns->parent) { if (ns == target_ns) return true; } diff --git a/security/commoncap.c b/security/commoncap.c index 6243aef5860e..2119421613f6 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -450,7 +450,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c if (!mnt_may_suid(bprm->file->f_path.mnt)) return 0; - if (!in_userns(current_user_ns(), bprm->file->f_path.mnt->mnt_sb->s_user_ns)) + if (!current_in_userns(bprm->file->f_path.mnt->mnt_sb->s_user_ns)) return 0; rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 06/18] Smack: Handle labels consistently in untrusted mounts
The SMACK64, SMACK64EXEC, and SMACK64MMAP labels are all handled differently in untrusted mounts. This is confusing and potentically problematic. Change this to handle them all the same way that SMACK64 is currently handled; that is, read the label from disk and check it at use time. For SMACK64 and SMACK64MMAP access is denied if the label does not match smk_root. To be consistent with suid, a SMACK64EXEC label which does not match smk_root will still allow execution of the file but will not run with the label supplied in the xattr. Signed-off-by: Seth Forshee Acked-by: Casey Schaufler --- security/smack/smack_lsm.c | 29 +++-- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 16cac04214e2..0e555f64ded0 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -921,6 +921,7 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) struct inode *inode = file_inode(bprm->file); struct task_smack *bsp = bprm->cred->security; struct inode_smack *isp; + struct superblock_smack *sbsp; int rc; if (bprm->cred_prepared) @@ -930,6 +931,11 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm) if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task) return 0; + sbsp = inode->i_sb->s_security; + if ((sbsp->smk_flags & SMK_SB_UNTRUSTED) && + isp->smk_task != sbsp->smk_root) + return 0; + if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { struct task_struct *tracer; rc = 0; @@ -1733,6 +1739,7 @@ static int smack_mmap_file(struct file *file, struct task_smack *tsp; struct smack_known *okp; struct inode_smack *isp; + struct superblock_smack *sbsp; int may; int mmay; int tmay; @@ -1744,6 +1751,10 @@ static int smack_mmap_file(struct file *file, isp = file_inode(file)->i_security; if (isp->smk_mmap == NULL) return 0; + sbsp = file_inode(file)->i_sb->s_security; + if (sbsp->smk_flags & SMK_SB_UNTRUSTED && + isp->smk_mmap != sbsp->smk_root) + return -EACCES; mkp = isp->smk_mmap; tsp = current_security(); @@ -3532,16 +3543,14 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) if (rc >= 0) transflag = SMK_INODE_TRANSMUTE; } - if (!(sbsp->smk_flags & SMK_SB_UNTRUSTED)) { - /* -* Don't let the exec or mmap label be "*" or "@". -*/ - skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); - if (IS_ERR(skp) || skp == &smack_known_star || - skp == &smack_known_web) - skp = NULL; - isp->smk_task = skp; - } + /* +* Don't let the exec or mmap label be "*" or "@". +*/ + skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); + if (IS_ERR(skp) || skp == &smack_known_star || + skp == &smack_known_web) + skp = NULL; + isp->smk_task = skp; skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); if (IS_ERR(skp) || skp == &smack_known_star || -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 18/18] fuse: Allow user namespace mounts
Signed-off-by: Seth Forshee --- fs/fuse/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index b7bdfdac3521..2fd338c199ce 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1201,7 +1201,7 @@ static void fuse_kill_sb_anon(struct super_block *sb) static struct file_system_type fuse_fs_type = { .owner = THIS_MODULE, .name = "fuse", - .fs_flags = FS_HAS_SUBTYPE, + .fs_flags = FS_HAS_SUBTYPE | FS_USERNS_MOUNT, .mount = fuse_mount, .kill_sb= fuse_kill_sb_anon, }; @@ -1233,7 +1233,7 @@ static struct file_system_type fuseblk_fs_type = { .name = "fuseblk", .mount = fuse_mount_blk, .kill_sb= fuse_kill_sb_blk, - .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE, + .fs_flags = FS_REQUIRES_DEV | FS_HAS_SUBTYPE | FS_USERNS_MOUNT, }; MODULE_ALIAS_FS("fuseblk"); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 09/18] fs: Refuse uid/gid changes which don't map into s_user_ns
Add checks to inode_change_ok to verify that uid and gid changes will map into the superblock's user namespace. If they do not fail with -EOVERFLOW. This cannot be overriden with ATTR_FORCE. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/attr.c | 11 +++ 1 file changed, 11 insertions(+) diff --git a/fs/attr.c b/fs/attr.c index 6530ced19697..55b46e3aa888 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -42,6 +42,17 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) return error; } + /* +* Verify that uid/gid changes are valid in the target namespace +* of the superblock. This cannot be overriden using ATTR_FORCE. +*/ + if (ia_valid & ATTR_UID && + from_kuid(inode->i_sb->s_user_ns, attr->ia_uid) == (uid_t)-1) + return -EOVERFLOW; + if (ia_valid & ATTR_GID && + from_kgid(inode->i_sb->s_user_ns, attr->ia_gid) == (gid_t)-1) + return -EOVERFLOW; + /* If force is set do it anyway. */ if (ia_valid & ATTR_FORCE) return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 03/18] fs: Treat foreign mounts as nosuid
From: Andy Lutomirski If a process gets access to a mount from a different user namespace, that process should not be able to take advantage of setuid files or selinux entrypoints from that filesystem. Prevent this by treating mounts from other mount namespaces and those not owned by current_user_ns() or an ancestor as nosuid. This will make it safer to allow more complex filesystems to be mounted in non-root user namespaces. This does not remove the need for MNT_LOCK_NOSUID. The setuid, setgid, and file capability bits can no longer be abused if code in a user namespace were to clear nosuid on an untrusted filesystem, but this patch, by itself, is insufficient to protect the system from abuse of files that, when execed, would increase MAC privilege. As a more concrete explanation, any task that can manipulate a vfsmount associated with a given user namespace already has capabilities in that namespace and all of its descendents. If they can cause a malicious setuid, setgid, or file-caps executable to appear in that mount, then that executable will only allow them to elevate privileges in exactly the set of namespaces in which they are already privileges. On the other hand, if they can cause a malicious executable to appear with a dangerous MAC label, running it could change the caller's security context in a way that should not have been possible, even inside the namespace in which the task is confined. As a hardening measure, this would have made CVE-2014-5207 much more difficult to exploit. Signed-off-by: Andy Lutomirski Signed-off-by: Seth Forshee Acked-by: James Morris Acked-by: Serge Hallyn --- fs/exec.c| 2 +- fs/namespace.c | 13 + include/linux/mount.h| 1 + security/commoncap.c | 2 +- security/selinux/hooks.c | 2 +- 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/fs/exec.c b/fs/exec.c index b06623a9347f..ea7311d72cc3 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1295,7 +1295,7 @@ static void bprm_fill_uid(struct linux_binprm *bprm) bprm->cred->euid = current_euid(); bprm->cred->egid = current_egid(); - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if (!mnt_may_suid(bprm->file->f_path.mnt)) return; if (task_no_new_privs(current)) diff --git a/fs/namespace.c b/fs/namespace.c index da70f7c4ece1..2101ce7b96ab 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3276,6 +3276,19 @@ found: return visible; } +bool mnt_may_suid(struct vfsmount *mnt) +{ + /* +* Foreign mounts (accessed via fchdir or through /proc +* symlinks) are always treated as if they are nosuid. This +* prevents namespaces from trusting potentially unsafe +* suid/sgid bits, file caps, or security labels that originate +* in other namespaces. +*/ + return !(mnt->mnt_flags & MNT_NOSUID) && check_mnt(real_mount(mnt)) && + in_userns(current_user_ns(), mnt->mnt_sb->s_user_ns); +} + static struct ns_common *mntns_get(struct task_struct *task) { struct ns_common *ns = NULL; diff --git a/include/linux/mount.h b/include/linux/mount.h index f822c3c11377..54a594d49733 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -81,6 +81,7 @@ extern void mntput(struct vfsmount *mnt); extern struct vfsmount *mntget(struct vfsmount *mnt); extern struct vfsmount *mnt_clone_internal(struct path *path); extern int __mnt_is_readonly(struct vfsmount *mnt); +extern bool mnt_may_suid(struct vfsmount *mnt); struct path; extern struct vfsmount *clone_private_mount(struct path *path); diff --git a/security/commoncap.c b/security/commoncap.c index 400aa224b491..6243aef5860e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -448,7 +448,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c if (!file_caps_enabled) return 0; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if (!mnt_may_suid(bprm->file->f_path.mnt)) return 0; if (!in_userns(current_user_ns(), bprm->file->f_path.mnt->mnt_sb->s_user_ns)) return 0; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d0cfaa9f19d0..a5b93df6553f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2171,7 +2171,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm, const struct task_security_struct *new_tsec) { int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); - int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID); + int nosuid = !mnt_may_suid(bprm->file->f_path.mnt); int rc; if (!nnp && !nosuid) -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 17/18] fuse: Restrict allow_other to the superblock's namespace or a descendant
Unprivileged users are normally restricted from mounting with the allow_other option by system policy, but this could be bypassed for a mount done with user namespace root permissions. In such cases allow_other should not allow users outside the userns to access the mount as doing so would give the unprivileged user the ability to manipulate processes it would otherwise be unable to manipulate. Restrict allow_other to apply to users in the same userns used at mount or a descendant of that namespace. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/fuse/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 8fd9fe4dcd43..24e4cdb554f1 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1015,7 +1015,7 @@ int fuse_allow_current_process(struct fuse_conn *fc) const struct cred *cred; if (fc->flags & FUSE_ALLOW_OTHER) - return 1; + return current_in_userns(fc->user_ns); cred = current_cred(); if (uid_eq(cred->euid, fc->user_id) && -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 08/18] cred: Reject inodes with invalid ids in set_create_file_as()
Using INVALID_[UG]ID for the LSM file creation context doesn't make sense, so return an error if the inode passed to set_create_file_as() has an invalid id. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- kernel/cred.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/cred.c b/kernel/cred.c index 71179a09c1d6..ff8606f77d90 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -689,6 +689,8 @@ EXPORT_SYMBOL(set_security_override_from_ctx); */ int set_create_files_as(struct cred *new, struct inode *inode) { + if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid)) + return -EINVAL; new->fsuid = inode->i_uid; new->fsgid = inode->i_gid; return security_kernel_create_files_as(new, inode); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 10/18] fs: Update posix_acl support to handle user namespace mounts
ids in on-disk ACLs should be converted to s_user_ns instead of init_user_ns as is done now. This introduces the possibility for id mappings to fail, and when this happens syscalls will return EOVERFLOW. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/posix_acl.c | 67 ++--- fs/xattr.c | 19 +--- include/linux/posix_acl_xattr.h | 17 --- 3 files changed, 70 insertions(+), 33 deletions(-) diff --git a/fs/posix_acl.c b/fs/posix_acl.c index 4adde1e2cbec..a29442eb4af8 100644 --- a/fs/posix_acl.c +++ b/fs/posix_acl.c @@ -595,59 +595,77 @@ EXPORT_SYMBOL_GPL(posix_acl_create); /* * Fix up the uids and gids in posix acl extended attributes in place. */ -static void posix_acl_fix_xattr_userns( +static int posix_acl_fix_xattr_userns( struct user_namespace *to, struct user_namespace *from, void *value, size_t size) { posix_acl_xattr_header *header = (posix_acl_xattr_header *)value; posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end; int count; - kuid_t uid; - kgid_t gid; + kuid_t kuid; + uid_t uid; + kgid_t kgid; + gid_t gid; if (!value) - return; + return 0; if (size < sizeof(posix_acl_xattr_header)) - return; + return 0; if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION)) - return; + return 0; count = posix_acl_xattr_count(size); if (count < 0) - return; + return 0; if (count == 0) - return; + return 0; for (end = entry + count; entry != end; entry++) { switch(le16_to_cpu(entry->e_tag)) { case ACL_USER: - uid = make_kuid(from, le32_to_cpu(entry->e_id)); - entry->e_id = cpu_to_le32(from_kuid(to, uid)); + kuid = make_kuid(from, le32_to_cpu(entry->e_id)); + if (!uid_valid(kuid)) + return -EOVERFLOW; + uid = from_kuid(to, kuid); + if (uid == (uid_t)-1) + return -EOVERFLOW; + entry->e_id = cpu_to_le32(uid); break; case ACL_GROUP: - gid = make_kgid(from, le32_to_cpu(entry->e_id)); - entry->e_id = cpu_to_le32(from_kgid(to, gid)); + kgid = make_kgid(from, le32_to_cpu(entry->e_id)); + if (!gid_valid(kgid)) + return -EOVERFLOW; + gid = from_kgid(to, kgid); + if (gid == (gid_t)-1) + return -EOVERFLOW; + entry->e_id = cpu_to_le32(gid); break; default: break; } } + + return 0; } -void posix_acl_fix_xattr_from_user(void *value, size_t size) +int +posix_acl_fix_xattr_from_user(struct user_namespace *target_ns, void *value, + size_t size) { - struct user_namespace *user_ns = current_user_ns(); - if (user_ns == &init_user_ns) - return; - posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size); + struct user_namespace *source_ns = current_user_ns(); + if (source_ns == target_ns) + return 0; + return posix_acl_fix_xattr_userns(target_ns, source_ns, value, size); } -void posix_acl_fix_xattr_to_user(void *value, size_t size) +int +posix_acl_fix_xattr_to_user(struct user_namespace *source_ns, void *value, + size_t size) { - struct user_namespace *user_ns = current_user_ns(); - if (user_ns == &init_user_ns) - return; - posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size); + struct user_namespace *target_ns = current_user_ns(); + if (target_ns == source_ns) + return 0; + return posix_acl_fix_xattr_userns(target_ns, source_ns, value, size); } /* @@ -782,7 +800,7 @@ posix_acl_xattr_get(const struct xattr_handler *handler, if (acl == NULL) return -ENODATA; - error = posix_acl_to_xattr(&init_user_ns, acl, value, size); + error = posix_acl_to_xattr(dentry->d_sb->s_user_ns, acl, value, size); posix_acl_release(acl); return error; @@ -810,7 +828,8 @@ posix_acl_xattr_set(const struct xattr_handler *handler, return -EPERM; if (value) { - acl = posix_acl_from_xattr(&init_user_ns, value, size); + acl = posix_acl_from_xattr(dentry->d_sb->s_user_ns, value, + size); if (IS_ERR(acl))
[PATCH RESEND v2 12/18] fs: Don't remove suid for CAP_FSETID in s_user_ns
Expand the check in should_remove_suid() to keep privileges for CAP_FSETID in s_user_ns rather than init_user_ns. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index 01c036fe1950..3e7c74da9304 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1684,7 +1684,8 @@ int should_remove_suid(struct dentry *dentry) if (unlikely((mode & S_ISGID) && (mode & S_IXGRP))) kill |= ATTR_KILL_SGID; - if (unlikely(kill && !capable(CAP_FSETID) && S_ISREG(mode))) + if (unlikely(kill && !ns_capable(dentry->d_sb->s_user_ns, CAP_FSETID) && +S_ISREG(mode))) return kill; return 0; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 16/18] fuse: Support fuse filesystems outside of init_user_ns
In order to support mounts from namespaces other than init_user_ns, fuse must translate uids and gids to/from the userns of the process servicing requests on /dev/fuse. This patch does that, with a couple of restrictions on the namespace: - The userns for the fuse connection is fixed to the namespace from which /dev/fuse is opened. - The namespace must be the same as s_user_ns. These restrictions simplify the implementation by avoiding the need to pass around userns references and by allowing fuse to rely on the checks in inode_change_ok for ownership changes. Either restriction could be relaxed in the future if needed. For cuse the namespace used for the connection is also simply current_user_ns() at the time /dev/cuse is opened. Signed-off-by: Seth Forshee --- fs/fuse/cuse.c | 3 ++- fs/fuse/dev.c| 13 - fs/fuse/dir.c| 14 +++--- fs/fuse/fuse_i.h | 6 +- fs/fuse/inode.c | 35 +++ 5 files changed, 45 insertions(+), 26 deletions(-) diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index eae2c11268bc..a10aca57bfe4 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "fuse_i.h" @@ -498,7 +499,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file) if (!cc) return -ENOMEM; - fuse_conn_init(&cc->fc); + fuse_conn_init(&cc->fc, current_user_ns()); fud = fuse_dev_alloc(&cc->fc); if (!fud) { diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index a4f6f30d6d86..11b4cb0a0e2f 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -127,8 +127,8 @@ static void __fuse_put_request(struct fuse_req *req) static void fuse_req_init_context(struct fuse_conn *fc, struct fuse_req *req) { - req->in.h.uid = from_kuid_munged(&init_user_ns, current_fsuid()); - req->in.h.gid = from_kgid_munged(&init_user_ns, current_fsgid()); + req->in.h.uid = from_kuid(fc->user_ns, current_fsuid()); + req->in.h.gid = from_kgid(fc->user_ns, current_fsgid()); req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns); } @@ -186,7 +186,8 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, __set_bit(FR_WAITING, &req->flags); if (for_background) __set_bit(FR_BACKGROUND, &req->flags); - if (req->in.h.pid == 0) { + if (req->in.h.pid == 0 || req->in.h.uid == (uid_t)-1 || + req->in.h.gid == (gid_t)-1) { fuse_put_request(fc, req); return ERR_PTR(-EOVERFLOW); } @@ -1248,7 +1249,8 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, struct fuse_in *in; unsigned reqsize; - if (task_active_pid_ns(current) != fc->pid_ns) + if (task_active_pid_ns(current) != fc->pid_ns || + current_user_ns() != fc->user_ns) return -EIO; restart: @@ -1880,7 +1882,8 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, struct fuse_req *req; struct fuse_out_header oh; - if (task_active_pid_ns(current) != fc->pid_ns) + if (task_active_pid_ns(current) != fc->pid_ns || + current_user_ns() != fc->user_ns) return -EIO; if (nbytes < sizeof(struct fuse_out_header)) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5e2e08712d3b..8fd9fe4dcd43 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -841,8 +841,8 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, stat->ino = attr->ino; stat->mode = (inode->i_mode & S_IFMT) | (attr->mode & 0); stat->nlink = attr->nlink; - stat->uid = make_kuid(&init_user_ns, attr->uid); - stat->gid = make_kgid(&init_user_ns, attr->gid); + stat->uid = inode->i_uid; + stat->gid = inode->i_gid; stat->rdev = inode->i_rdev; stat->atime.tv_sec = attr->atime; stat->atime.tv_nsec = attr->atimensec; @@ -1455,17 +1455,17 @@ static bool update_mtime(unsigned ivalid, bool trust_local_mtime) return true; } -static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, - bool trust_local_cmtime) +static void iattr_to_fattr(struct fuse_conn *fc, struct iattr *iattr, + struct fuse_setattr_in *arg, bool trust_local_cmtime) { unsigned ivalid = iattr->ia_valid; if (ivalid & ATTR_MODE) arg->valid |= FATTR_MODE, arg->mode = iattr->ia_mode; if (ivalid & ATTR_UID) - arg->valid |= FATTR_UID,arg->uid = from_kuid(&init_user_ns, iattr->ia_uid); + arg->valid |= FATTR_UID,arg->uid = from_kuid(fc->user_ns, iattr->ia_uid); if (ivalid & ATTR_GID) - arg->valid |= FATTR_GID,arg->gid = from_kgid(&init_user_ns, iattr->ia_gid); + arg->valid |= FATTR_GID,arg->gid = from_kgid(fc->user_ns, i
[PATCH RESEND v2 14/18] capabilities: Allow privileged user in s_user_ns to set security.* xattrs
A privileged user in s_user_ns will generally have the ability to manipulate the backing store and insert security.* xattrs into the filesystem directly. Therefore the kernel must be prepared to handle these xattrs from unprivileged mounts, and it makes little sense for commoncap to prevent writing these xattrs to the filesystem. The capability and LSM code have already been updated to appropriately handle xattrs from unprivileged mounts, so it is safe to loosen this restriction on setting xattrs. The exception to this logic is that writing xattrs to a mounted filesystem may also cause the LSM inode_post_setxattr or inode_setsecurity callbacks to be invoked. SELinux will deny the xattr update by virtue of applying mountpoint labeling to unprivileged userns mounts, and Smack will deny the writes for any user without global CAP_MAC_ADMIN, so loosening the capability check in commoncap is safe in this respect as well. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- security/commoncap.c | 12 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/security/commoncap.c b/security/commoncap.c index 2119421613f6..d6c80c19c449 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -653,15 +653,17 @@ int cap_bprm_secureexec(struct linux_binprm *bprm) int cap_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { + struct user_namespace *user_ns = dentry->d_sb->s_user_ns; + if (!strcmp(name, XATTR_NAME_CAPS)) { - if (!capable(CAP_SETFCAP)) + if (!ns_capable(user_ns, CAP_SETFCAP)) return -EPERM; return 0; } if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) && - !capable(CAP_SYS_ADMIN)) + !ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; return 0; } @@ -679,15 +681,17 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name, */ int cap_inode_removexattr(struct dentry *dentry, const char *name) { + struct user_namespace *user_ns = dentry->d_sb->s_user_ns; + if (!strcmp(name, XATTR_NAME_CAPS)) { - if (!capable(CAP_SETFCAP)) + if (!ns_capable(user_ns, CAP_SETFCAP)) return -EPERM; return 0; } if (!strncmp(name, XATTR_SECURITY_PREFIX, sizeof(XATTR_SECURITY_PREFIX) - 1) && - !capable(CAP_SYS_ADMIN)) + !ns_capable(user_ns, CAP_SYS_ADMIN)) return -EPERM; return 0; } -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 13/18] fs: Allow superblock owner to access do_remount_sb()
Superblock level remounts are currently restricted to global CAP_SYS_ADMIN, as is the path for changing the root mount to read only on umount. Loosen both of these permission checks to also allow CAP_SYS_ADMIN in any namespace which is privileged towards the userns which originally mounted the filesystem. Signed-off-by: Seth Forshee Acked-by: "Eric W. Biederman" Acked-by: Serge Hallyn --- fs/namespace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 18fc58760aec..b00a765895e7 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1510,7 +1510,7 @@ static int do_umount(struct mount *mnt, int flags) * Special case for "unmounting" root ... * we just try to remount it readonly. */ - if (!capable(CAP_SYS_ADMIN)) + if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) return -EPERM; down_write(&sb->s_umount); if (!(sb->s_flags & MS_RDONLY)) @@ -2199,7 +2199,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, down_write(&sb->s_umount); if (flags & MS_BIND) err = change_mount_flags(path->mnt, flags); - else if (!capable(CAP_SYS_ADMIN)) + else if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) err = -EPERM; else err = do_remount_sb(sb, flags, data, 0); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 04/18] selinux: Add support for unprivileged mounts from user namespaces
Security labels from unprivileged mounts in user namespaces must be ignored. Force superblocks from user namespaces whose labeling behavior is to use xattrs to use mountpoint labeling instead. For the mountpoint label, default to converting the current task context into a form suitable for file objects, but also allow the policy writer to specify a different label through policy transition rules. Pieced together from code snippets provided by Stephen Smalley. Signed-off-by: Seth Forshee Acked-by: Stephen Smalley Acked-by: James Morris --- security/selinux/hooks.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a5b93df6553f..5fedc36dd6b2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -756,6 +756,28 @@ static int selinux_set_mnt_opts(struct super_block *sb, goto out; } } + + /* +* If this is a user namespace mount, no contexts are allowed +* on the command line and security labels must be ignored. +*/ + if (sb->s_user_ns != &init_user_ns) { + if (context_sid || fscontext_sid || rootcontext_sid || + defcontext_sid) { + rc = -EACCES; + goto out; + } + if (sbsec->behavior == SECURITY_FS_USE_XATTR) { + sbsec->behavior = SECURITY_FS_USE_MNTPOINT; + rc = security_transition_sid(current_sid(), current_sid(), +SECCLASS_FILE, NULL, +&sbsec->mntpoint_sid); + if (rc) + goto out; + } + goto out_set_opts; + } + /* sets the context of the superblock for the fs being mounted. */ if (fscontext_sid) { rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred); @@ -824,6 +846,7 @@ static int selinux_set_mnt_opts(struct super_block *sb, sbsec->def_sid = defcontext_sid; } +out_set_opts: rc = sb_finish_set_opts(sb); out: mutex_unlock(&sbsec->lock); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 15/18] fuse: Add support for pid namespaces
If the userspace process servicing fuse requests is running in a pid namespace then pids passed via the fuse fd need to be translated relative to that namespace. Capture the pid namespace in use when the filesystem is mounted and use this for pid translation. Since no use case currently exists for changing namespaces all translations are done relative to the pid namespace in use when /dev/fuse is opened. Mounting or /dev/fuse IO from another namespace will return errors. Requests from processes whose pid cannot be translated into the target namespace are not permitted, except for requests allocated via fuse_get_req_nofail_nopages. For no-fail requests in.h.pid will be 0 if the pid translation fails. File locking changes based on previous work done by Eric Biederman. Signed-off-by: Seth Forshee Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c| 19 +++ fs/fuse/file.c | 22 +- fs/fuse/fuse_i.h | 4 fs/fuse/inode.c | 3 +++ 4 files changed, 39 insertions(+), 9 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index ebb5e37455a0..a4f6f30d6d86 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -19,6 +19,7 @@ #include #include #include +#include MODULE_ALIAS_MISCDEV(FUSE_MINOR); MODULE_ALIAS("devname:fuse"); @@ -124,11 +125,11 @@ static void __fuse_put_request(struct fuse_req *req) atomic_dec(&req->count); } -static void fuse_req_init_context(struct fuse_req *req) +static void fuse_req_init_context(struct fuse_conn *fc, struct fuse_req *req) { req->in.h.uid = from_kuid_munged(&init_user_ns, current_fsuid()); req->in.h.gid = from_kgid_munged(&init_user_ns, current_fsgid()); - req->in.h.pid = current->pid; + req->in.h.pid = pid_nr_ns(task_pid(current), fc->pid_ns); } void fuse_set_initialized(struct fuse_conn *fc) @@ -181,10 +182,14 @@ static struct fuse_req *__fuse_get_req(struct fuse_conn *fc, unsigned npages, goto out; } - fuse_req_init_context(req); + fuse_req_init_context(fc, req); __set_bit(FR_WAITING, &req->flags); if (for_background) __set_bit(FR_BACKGROUND, &req->flags); + if (req->in.h.pid == 0) { + fuse_put_request(fc, req); + return ERR_PTR(-EOVERFLOW); + } return req; @@ -274,7 +279,7 @@ struct fuse_req *fuse_get_req_nofail_nopages(struct fuse_conn *fc, if (!req) req = get_reserved_req(fc, file); - fuse_req_init_context(req); + fuse_req_init_context(fc, req); __set_bit(FR_WAITING, &req->flags); __clear_bit(FR_BACKGROUND, &req->flags); return req; @@ -1243,6 +1248,9 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file, struct fuse_in *in; unsigned reqsize; + if (task_active_pid_ns(current) != fc->pid_ns) + return -EIO; + restart: spin_lock(&fiq->waitq.lock); err = -EAGAIN; @@ -1872,6 +1880,9 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, struct fuse_req *req; struct fuse_out_header oh; + if (task_active_pid_ns(current) != fc->pid_ns) + return -EIO; + if (nbytes < sizeof(struct fuse_out_header)) return -EINVAL; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index e0faf8f2c868..a6c7484c94ee 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2061,7 +2061,8 @@ static int fuse_direct_mmap(struct file *file, struct vm_area_struct *vma) return generic_file_mmap(file, vma); } -static int convert_fuse_file_lock(const struct fuse_file_lock *ffl, +static int convert_fuse_file_lock(struct fuse_conn *fc, + const struct fuse_file_lock *ffl, struct file_lock *fl) { switch (ffl->type) { @@ -2076,7 +2077,14 @@ static int convert_fuse_file_lock(const struct fuse_file_lock *ffl, fl->fl_start = ffl->start; fl->fl_end = ffl->end; - fl->fl_pid = ffl->pid; + + /* +* Convert pid into the caller's pid namespace. If the pid +* does not map into the namespace fl_pid will get set to 0. +*/ + rcu_read_lock(); + fl->fl_pid = pid_vnr(find_pid_ns(ffl->pid, fc->pid_ns)); + rcu_read_unlock(); break; default: @@ -2125,7 +2133,7 @@ static int fuse_getlk(struct file *file, struct file_lock *fl) args.out.args[0].value = &outarg; err = fuse_simple_request(fc, &args); if (!err) - err = convert_fuse_file_lock(&outarg.lk, fl); + err = convert_fuse_file_lock(fc, &outarg.lk, fl); return err; } @@ -2137,7 +2145,8 @@ static int fuse_setlk(struct file *file, struct file_lock *fl, int flock) FUSE_ARGS(args); struct fuse_lk_in inarg; int opcode = (fl->fl_
[PATCH RESEND v2 11/18] fs: Ensure the mounter of a filesystem is privileged towards its inodes
The mounter of a filesystem should be privileged towards the inodes of that filesystem. Extend the checks in inode_owner_or_capable() and capable_wrt_inode_uidgid() to permit access by users priviliged in the user namespace of the inode's superblock. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/inode.c | 3 +++ kernel/capability.c | 13 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/inode.c b/fs/inode.c index 1be5f9003eb3..01c036fe1950 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1962,6 +1962,9 @@ bool inode_owner_or_capable(const struct inode *inode) ns = current_user_ns(); if (ns_capable(ns, CAP_FOWNER) && kuid_has_mapping(ns, inode->i_uid)) return true; + + if (ns_capable(inode->i_sb->s_user_ns, CAP_FOWNER)) + return true; return false; } EXPORT_SYMBOL(inode_owner_or_capable); diff --git a/kernel/capability.c b/kernel/capability.c index 45432b54d5c6..5137a38a5670 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -437,13 +437,18 @@ EXPORT_SYMBOL(file_ns_capable); * * Return true if the current task has the given capability targeted at * its own user namespace and that the given inode's uid and gid are - * mapped into the current user namespace. + * mapped into the current user namespace, or if the current task has + * the capability towards the user namespace of the inode's superblock. */ bool capable_wrt_inode_uidgid(const struct inode *inode, int cap) { - struct user_namespace *ns = current_user_ns(); + struct user_namespace *ns; - return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && - kgid_has_mapping(ns, inode->i_gid); + ns = current_user_ns(); + if (ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && + kgid_has_mapping(ns, inode->i_gid)) + return true; + + return ns_capable(inode->i_sb->s_user_ns, cap); } EXPORT_SYMBOL(capable_wrt_inode_uidgid); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH RESEND v2 01/18] block_dev: Support checking inode permissions in lookup_bdev()
When looking up a block device by path no permission check is done to verify that the user has access to the block device inode at the specified path. In some cases it may be necessary to check permissions towards the inode, such as allowing unprivileged users to mount block devices in user namespaces. Add an argument to lookup_bdev() to optionally perform this permission check. A value of 0 skips the permission check and behaves the same as before. A non-zero value specifies the mask of access rights required towards the inode at the specified path. The check is always skipped if the user has CAP_SYS_ADMIN. All callers of lookup_bdev() currently pass a mask of 0, so this patch results in no functional change. Subsequent patches will add permission checks where appropriate. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- drivers/md/bcache/super.c | 2 +- drivers/md/dm-table.c | 2 +- drivers/mtd/mtdsuper.c| 2 +- fs/block_dev.c| 13 ++--- fs/quota/quota.c | 2 +- include/linux/fs.h| 2 +- 6 files changed, 15 insertions(+), 8 deletions(-) diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c index 679a093a3bf6..e8287b0d1dac 100644 --- a/drivers/md/bcache/super.c +++ b/drivers/md/bcache/super.c @@ -1926,7 +1926,7 @@ static ssize_t register_bcache(struct kobject *k, struct kobj_attribute *attr, sb); if (IS_ERR(bdev)) { if (bdev == ERR_PTR(-EBUSY)) { - bdev = lookup_bdev(strim(path)); + bdev = lookup_bdev(strim(path), 0); mutex_lock(&bch_register_lock); if (!IS_ERR(bdev) && bch_is_open(bdev)) err = "device already registered"; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 061152a43730..81c60b2495ed 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -380,7 +380,7 @@ int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode, BUG_ON(!t); /* convert the path to a device */ - bdev = lookup_bdev(path); + bdev = lookup_bdev(path, 0); if (IS_ERR(bdev)) { dev = name_to_dev_t(path); if (!dev) diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index 20c02a3b7417..b5b60e1af31c 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -176,7 +176,7 @@ struct dentry *mount_mtd(struct file_system_type *fs_type, int flags, /* try the old way - the hack where we allowed users to mount * /dev/mtdblock$(n) but didn't actually _use_ the blockdev */ - bdev = lookup_bdev(dev_name); + bdev = lookup_bdev(dev_name, 0); if (IS_ERR(bdev)) { ret = PTR_ERR(bdev); pr_debug("MTDSB: lookup_bdev() returned %d\n", ret); diff --git a/fs/block_dev.c b/fs/block_dev.c index f90d91efa1b4..3ebbde85d898 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1426,7 +1426,7 @@ struct block_device *blkdev_get_by_path(const char *path, fmode_t mode, struct block_device *bdev; int err; - bdev = lookup_bdev(path); + bdev = lookup_bdev(path, 0); if (IS_ERR(bdev)) return bdev; @@ -1736,12 +1736,14 @@ EXPORT_SYMBOL(ioctl_by_bdev); /** * lookup_bdev - lookup a struct block_device by name * @pathname: special file representing the block device + * @mask: rights to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) * * Get a reference to the blockdevice at @pathname in the current * namespace if possible and return it. Return ERR_PTR(error) - * otherwise. + * otherwise. If @mask is non-zero, check for access rights to the + * inode at @pathname. */ -struct block_device *lookup_bdev(const char *pathname) +struct block_device *lookup_bdev(const char *pathname, int mask) { struct block_device *bdev; struct inode *inode; @@ -1756,6 +1758,11 @@ struct block_device *lookup_bdev(const char *pathname) return ERR_PTR(error); inode = d_backing_inode(path.dentry); + if (mask != 0 && !capable(CAP_SYS_ADMIN)) { + error = __inode_permission(inode, mask); + if (error) + goto fail; + } error = -ENOTBLK; if (!S_ISBLK(inode->i_mode)) goto fail; diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 3746367098fd..a40eaecbd5cc 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -733,7 +733,7 @@ static struct super_block *quotactl_block(const char __user *special, int cmd) if (IS_ERR(tmp)) return ERR_CAST(tmp); - bdev = lookup_bdev(tmp->name); + bdev = lookup_bdev(tmp->name, 0); putname(tmp); if (IS_ERR(bdev)) return ERR_CAST(bdev); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8a17c5649ef2..879ec382fd88 100644 --- a/include
[PATCH RESEND v2 02/18] block_dev: Check permissions towards block device inode when mounting
Unprivileged users should not be able to mount block devices when they lack sufficient privileges towards the block device inode. Update blkdev_get_by_path() to validate that the user has the required access to the inode at the specified path. The check will be skipped for CAP_SYS_ADMIN, so privileged mounts will continue working as before. Signed-off-by: Seth Forshee Acked-by: Serge Hallyn --- fs/block_dev.c | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/block_dev.c b/fs/block_dev.c index 3ebbde85d898..4fdb6ab59816 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1424,9 +1424,14 @@ struct block_device *blkdev_get_by_path(const char *path, fmode_t mode, void *holder) { struct block_device *bdev; + int perm = 0; int err; - bdev = lookup_bdev(path, 0); + if (mode & FMODE_READ) + perm |= MAY_READ; + if (mode & FMODE_WRITE) + perm |= MAY_WRITE; + bdev = lookup_bdev(path, perm); if (IS_ERR(bdev)) return bdev; -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 5/5] firmware: add an extensible system data helpers
On Wed, Dec 23, 2015 at 1:34 PM, Luis R. Rodriguez wrote: > From: "Luis R. Rodriguez" > > The firmware API has evolved over the years slowly, as it > grows we extend it by adding new routines or at times we extend > existing routines with more or less arguments. This doesn't scale > well, when new arguments are added to existing routines it means > we need to traverse the kernel with a slew of collateral > evolutions to adjust old driver users. The firmware API is also > now being used for things outside of the scope of what typically > would be considered "firmware", an example here is the p54 driver > enables users to provide a custom EEPROM through this interface. > Another example is optional CPU microcode updates. > > There are other subsystems which would like to make use of the > APIs for similar things but have different requirements and > criteria which they'd like to be met for the requested file. > If different requirements are needed it would again mean adding > more arguments and making a slew of collateral evolutions, or > adding yet-another-new-API-call. > > Instead of extending the existing firmware API even more this > provides an new extensible API which can be used to supercede the > old firmware API without causing a series of collateral evolutions > as requirements grow. This leaves the old firmware API as-is, > ignores all consideration for usermode-helpers, labels the new API > to reflect its broad use outside of the scope of firmware: system > data helpers, and builds on top of the original firmware core code. > > The new extensible "system data" set of helpers accepts that there > really are only two types of requests for accessing system data: > > a) synchronous requests > b) asynchronous requests > > Both of these requests may have a different set of requirements > which must be met. These requirements can simply be passed as a > descriptors to each type of request. The descriptor can be extended > over time to support different requirements as the kernel evolves. > > Using the new system data helpers is only necessary if you have > requirements outside of what the existing old firmware API accepts. > We encourage developers to leave the old API as-is and extend the > new descriptors and system data code to provide support for new > features. > > A few simple features added as part of the new set of system data > request APIs, other than making the new API easily extensible for > the future: > > - By default the kernel will free the system data file for you after >your callbacks are called, you however are allowed to request to that >you wish to keep the system data file on the descriptor. This is >dealt with by requiring a consumer callback for the system data file. > - Allow both asynchronous and synchronous request to specify that system data >files are optional. With the old APIs we had added one full API call, >request_firmware_direct() just for this purpose -- although it should be >noted another of its goal was to also skip the usermode helper. >The system data request APIs allow for you to annotate that a system >data file is optional for both synchronous or asynchronous requests >through the same two basic set of APIs. > - Usermode helpers are completely ignored, always > - The system data request APIs currently match the old synchronous firmware >API calls to refcounted firmware_class module, but it should be easy >to add support now to enable also refcounting the caller's module >should it be be needed. Likewise the system data request APIs match the >old asynchronous firmware API call and refcounts the caller's module. > > In order to try to help phase out user mode helpers this makes no use of > the old user mode helper code *at all*, and if we wish to can easily > phase this code out with time then. So these are basically wrappers around the existing firmware loading routines? -Kees > > Signed-off-by: Luis R. Rodriguez > --- > Documentation/firmware_class/system_data.txt | 79 > MAINTAINERS | 4 +- > drivers/base/firmware_class.c| 291 > +++ > include/linux/sysdata.h | 212 +++ > 4 files changed, 585 insertions(+), 1 deletion(-) > create mode 100644 Documentation/firmware_class/system_data.txt > create mode 100644 include/linux/sysdata.h > > diff --git a/Documentation/firmware_class/system_data.txt > b/Documentation/firmware_class/system_data.txt > new file mode 100644 > index ..ab509243455a > --- /dev/null > +++ b/Documentation/firmware_class/system_data.txt > @@ -0,0 +1,79 @@ > +System data requests API > + > + > +As the kernel evolves we keep extending the firmware_class set of APIs > +with more or less arguments, this creates a slew of collateral evolutions. > +The set of users of firmware request APIs has also grown now to include > +users
Re: [PATCH v3 1/5] firmware: generalize "firmware" as "system data" helpers
On Wed, Dec 23, 2015 at 1:34 PM, Luis R. Rodriguez wrote: > From: "Luis R. Rodriguez" > > Historically firmware_class code was added to help > get device driver firmware binaries but these days > request_firmware*() helpers are being repurposed for > general system data needed by the kernel. > > Annotate this before we extend firmare_class more, > as this is expected. We want to generalize the code > as much as possible. > > Cc: Rusty Russell > Cc: Andrew Morton > Cc: Greg Kroah-Hartman > Cc: David Howells > Cc: Kees Cook > Cc: Casey Schaufler > Cc: Ming Lei > Cc: Takashi Iwai > Cc: Vojtěch Pavlík > Cc: Kyle McMartin > Cc: Matthew Garrett > Cc: linux-ker...@vger.kernel.org > Signed-off-by: Luis R. Rodriguez > --- > drivers/base/firmware_class.c | 8 > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c > index 8524450e75bd..6f5fcda71a60 100644 > --- a/drivers/base/firmware_class.c > +++ b/drivers/base/firmware_class.c > @@ -353,15 +353,15 @@ static int fw_get_filesystem_firmware(struct device > *device, > rc = fw_read_file_contents(file, buf); > fput(file); > if (rc) > - dev_warn(device, "firmware, attempted to load %s, but > failed with error %d\n", > - path, rc); > + dev_warn(device, "system data, attempted to load %s, > but failed with error %d\n", > +path, rc); Since dev_warn should already be prefixing the string, I would think "firmware, " should just be removed entirely instead of replaced? -Kees > else > break; > } > __putname(path); > > if (!rc) { > - dev_dbg(device, "firmware: direct-loading firmware %s\n", > + dev_dbg(device, "system data: direct-loading firmware %s\n", > buf->fw_id); > mutex_lock(&fw_lock); > set_bit(FW_STATUS_DONE, &buf->status); > @@ -1051,7 +1051,7 @@ _request_firmware_prepare(struct firmware **firmware_p, > const char *name, > } > > if (fw_get_builtin_firmware(firmware, name)) { > - dev_dbg(device, "firmware: using built-in firmware %s\n", > name); > + dev_dbg(device, "system data: using built-in system > data%s\n", name); > return 0; /* assigned */ > } > > -- > 2.6.2 > -- Kees Cook Chrome OS & Brillo Security -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 2/5] firmware: move completing fw into a helper
On Wed, Dec 23, 2015 at 4:34 PM, Luis R. Rodriguez wrote: > From: "Luis R. Rodriguez" > > This will be re-used later through a new extensible interface. > > Signed-off-by: Luis R. Rodriguez Reviewed-by: Josh Boyer josh > --- > drivers/base/firmware_class.c | 14 ++ > 1 file changed, 10 insertions(+), 4 deletions(-) > > diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c > index 6f5fcda71a60..d8148aa89b01 100644 > --- a/drivers/base/firmware_class.c > +++ b/drivers/base/firmware_class.c > @@ -322,6 +322,15 @@ fail: > return rc; > } > > +static void fw_finish_direct_load(struct device *device, > + struct firmware_buf *buf) > +{ > + mutex_lock(&fw_lock); > + set_bit(FW_STATUS_DONE, &buf->status); > + complete_all(&buf->completion); > + mutex_unlock(&fw_lock); > +} > + > static int fw_get_filesystem_firmware(struct device *device, >struct firmware_buf *buf) > { > @@ -363,10 +372,7 @@ static int fw_get_filesystem_firmware(struct device > *device, > if (!rc) { > dev_dbg(device, "system data: direct-loading firmware %s\n", > buf->fw_id); > - mutex_lock(&fw_lock); > - set_bit(FW_STATUS_DONE, &buf->status); > - complete_all(&buf->completion); > - mutex_unlock(&fw_lock); > + fw_finish_direct_load(device, buf); > } > > return rc; > -- > 2.6.2 > -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 3/5] firmware: fold successful fw read early
On Wed, Dec 23, 2015 at 4:34 PM, Luis R. Rodriguez wrote: > From: David Howells > > We'll be folding in some more checks on fw_read_file_contents(), > this will make the success case easier to follow. > > Signed-off-by: David Howells > Signed-off-by: Luis R. Rodriguez Reviewed-by: Josh Boyer > --- > drivers/base/firmware_class.c | 16 +++- > 1 file changed, 7 insertions(+), 9 deletions(-) > > diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c > index d8148aa89b01..e10a5349aa61 100644 > --- a/drivers/base/firmware_class.c > +++ b/drivers/base/firmware_class.c > @@ -361,20 +361,18 @@ static int fw_get_filesystem_firmware(struct device > *device, > continue; > rc = fw_read_file_contents(file, buf); > fput(file); > - if (rc) > + if (rc == 0) { > + dev_dbg(device, "system data: direct-loading firmware > %s\n", > + buf->fw_id); > + fw_finish_direct_load(device, buf); > + goto out; > + } else > dev_warn(device, "system data, attempted to load %s, > but failed with error %d\n", > path, rc); > - else > - break; > } > +out: > __putname(path); > > - if (!rc) { > - dev_dbg(device, "system data: direct-loading firmware %s\n", > - buf->fw_id); > - fw_finish_direct_load(device, buf); > - } > - > return rc; > } > > -- > 2.6.2 > -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH 0/4] X.509: Fix time handling
Here's a set of patches that fix X.509 time handling in three ways: (1) Fix leap year handling. (2) Add leap second handling (where you get a time of 23:59:60). (3) Add end-of-day midnight encoding (where you get a time of 24:00:00). David --- David Howells (4): X.509: Fix leap year handling again Handle ISO 8601 leap seconds and encodings of midnight in mktime64() X.509: Support leap seconds X.509: Handle midnight alternative notation in GeneralizedTime crypto/asymmetric_keys/x509_cert_parser.c | 12 ++-- kernel/time/time.c|9 - 2 files changed, 14 insertions(+), 7 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[RFC PATCH 1/4] X.509: Fix leap year handling again
There are still a couple of minor issues in the X.509 leap year handling: (1) To avoid doing a modulus-by-400 in addition to a modulus-by-100 when determining whether the year is a leap year or not, I divided the year by 100 after doing the modulus-by-100, thereby letting the compiler do one instruction for both, and then did a modulus-by-4. Unfortunately, I then passed the now-modified year value to mktime64() to construct a time value. Since this isn't a fast path and since mktime64() does a bunch of divisions, just condense down to "% 400". It's also easier to read. (2) The default month length for any February where the year doesn't divide by four exactly is obtained from the month_length[] array where the value is 29, not 28. This is fixed by altering the table. Reported-by: Rudolf Polzer Signed-off-by: David Howells Acked-By: David Woodhouse cc: sta...@vger.kernel.org --- crypto/asymmetric_keys/x509_cert_parser.c |8 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 021d39c0ba75..13c4e5a5fe8c 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -494,7 +494,7 @@ int x509_decode_time(time64_t *_t, size_t hdrlen, unsigned char tag, const unsigned char *value, size_t vlen) { - static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30, + static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; const unsigned char *p = value; unsigned year, mon, day, hour, min, sec, mon_len; @@ -540,9 +540,9 @@ int x509_decode_time(time64_t *_t, size_t hdrlen, if (year % 4 == 0) { mon_len = 29; if (year % 100 == 0) { - year /= 100; - if (year % 4 != 0) - mon_len = 28; + mon_len = 28; + if (year % 400 == 0) + mon_len = 29; } } } -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[RFC PATCH 2/4] Handle ISO 8601 leap seconds and encodings of midnight in mktime64()
Handle the following ISO 8601 features in mktime64(): (1) Leap seconds. Leap seconds are indicated by the seconds parameter being the value 60. Handle this by treating it the same as 00 of the following minute. (2) Alternate encodings of midnight. Two different encodings of midnight are permitted - 00:00:00 and 24:00:00 - the first is midnight today and the second is midnight tomorrow and is exactly equivalent to the first with tomorrow's date. As it happens, we don't actually need to change mktime64() to handle either of these - just comment them as valid parameters. These facility will be used by the X.509 parser. Doing it in mktime64() makes the policy common to the whole kernel and easier to find. Signed-off-by: David Howells cc: Arnd Bergmann cc: John Stultz --- kernel/time/time.c |9 - 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/time/time.c b/kernel/time/time.c index 86751c68e08d..be115b020d27 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -322,6 +322,13 @@ EXPORT_SYMBOL(timespec_trunc); * -year/100+year/400 terms, and add 10.] * * This algorithm was first published by Gauss (I think). + * + * A leap second can be indicated by calling this function with sec as + * 60 (allowable under ISO 8601). The leap second is treated the same + * as the following second since they don't exist in UNIX time. + * + * An encoding of midnight at the end of the day as 24:00:00 - ie. midnight + * tomorrow - (allowable under ISO 8601) is supported. */ time64_t mktime64(const unsigned int year0, const unsigned int mon0, const unsigned int day, const unsigned int hour, @@ -338,7 +345,7 @@ time64_t mktime64(const unsigned int year0, const unsigned int mon0, return time64_t) (year/4 - year/100 + year/400 + 367*mon/12 + day) + year*365 - 719499 - )*24 + hour /* now have hours */ + )*24 + hour /* now have hours - midnight tomorrow handled here */ )*60 + min /* now have minutes */ )*60 + sec; /* finally seconds */ } -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[RFC PATCH 4/4] X.509: Handle midnight alternative notation in GeneralizedTime
The ASN.1 GeneralizedTime object carries an ISO 8601 format date and time. The time is permitted to show midnight as 00:00 or 24:00 (the latter being equivalent of 00:00 of the following day). The permitted value is checked in x509_decode_time() but the actual handling is left to mktime64(). Without this patch, certain X.509 certificates will be rejected and could lead to an unbootable kernel. Note that with this patch we also permit any 24:mm:ss time and extend this to UTCTime, which whilst not strictly correct don't permit much leeway in fiddling date strings. Reported-by: Rudolf Polzer Signed-off-by: David Howells cc: David Woodhouse cc: John Stultz cc: Arnd Bergmann --- crypto/asymmetric_keys/x509_cert_parser.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 3379c0ba3988..70ed0852fdb2 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -548,7 +548,7 @@ int x509_decode_time(time64_t *_t, size_t hdrlen, } if (day < 1 || day > mon_len || - hour > 23 || + hour > 24 || /* ISO 8601 permits 24:00:00 as midnight tomorrow */ min > 59 || sec > 60) /* ISO 8601 permits leap seconds [X.680 46.3] */ goto invalid_time; -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[RFC PATCH 3/4] X.509: Support leap seconds
The format of ASN.1 GeneralizedTime seems to be specified by ISO 8601 [X.680 46.3] and this apparently supports leap seconds (ie. the seconds field is 60). It's not entirely clear that ASN.1 expects it, but we can relax the seconds check slightly for GeneralizedTime. This results in us passing a time with sec as 60 to mktime64(), which handles it as being a duplicate of the 0th second of the next minute. We can't really do otherwise without giving the kernel much greater knowledge of where all the leap seconds are. Unfortunately, this would require change the mapping of the kernel's current-time-in-seconds. UTCTime, however, only supports a seconds value in the range 00-59, but for the sake of simplicity allow this with UTCTime also. Without this patch, certain X.509 certificates will be rejected, potentially making a kernel unbootable. Reported-by: Rudolf Polzer Signed-off-by: David Howells cc: Arnd Bergmann cc: David Woodhouse cc: John Stultz --- crypto/asymmetric_keys/x509_cert_parser.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 13c4e5a5fe8c..3379c0ba3988 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -550,7 +550,7 @@ int x509_decode_time(time64_t *_t, size_t hdrlen, if (day < 1 || day > mon_len || hour > 23 || min > 59 || - sec > 59) + sec > 60) /* ISO 8601 permits leap seconds [X.680 46.3] */ goto invalid_time; *_t = mktime64(year, mon, day, hour, min, sec); -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html