aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext3
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 20:42:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-05-23 20:42:39 -0400
commit644473e9c60c1ff4f6351fed637a6e5551e3dce7 (patch)
tree10316518bedc735a2c6552886658d69dfd9f1eb0 /fs/ext3
parentfb827ec68446c83e9e8754fa9b55aed27ecc4661 (diff)
parent4b06a81f1daee668fbd6de85557bfb36dd36078f (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull user namespace enhancements from Eric Biederman: "This is a course correction for the user namespace, so that we can reach an inexpensive, maintainable, and reasonably complete implementation. Highlights: - Config guards make it impossible to enable the user namespace and code that has not been converted to be user namespace safe. - Use of the new kuid_t type ensures the if you somehow get past the config guards the kernel will encounter type errors if you enable user namespaces and attempt to compile in code whose permission checks have not been updated to be user namespace safe. - All uids from child user namespaces are mapped into the initial user namespace before they are processed. Removing the need to add an additional check to see if the user namespace of the compared uids remains the same. - With the user namespaces compiled out the performance is as good or better than it is today. - For most operations absolutely nothing changes performance or operationally with the user namespace enabled. - The worst case performance I could come up with was timing 1 billion cache cold stat operations with the user namespace code enabled. This went from 156s to 164s on my laptop (or 156ns to 164ns per stat operation). - (uid_t)-1 and (gid_t)-1 are reserved as an internal error value. Most uid/gid setting system calls treat these value specially anyway so attempting to use -1 as a uid would likely cause entertaining failures in userspace. - If setuid is called with a uid that can not be mapped setuid fails. I have looked at sendmail, login, ssh and every other program I could think of that would call setuid and they all check for and handle the case where setuid fails. - If stat or a similar system call is called from a context in which we can not map a uid we lie and return overflowuid. The LFS experience suggests not lying and returning an error code might be better, but the historical precedent with uids is different and I can not think of anything that would break by lying about a uid we can't map. - Capabilities are localized to the current user namespace making it safe to give the initial user in a user namespace all capabilities. My git tree covers all of the modifications needed to convert the core kernel and enough changes to make a system bootable to runlevel 1." Fix up trivial conflicts due to nearby independent changes in fs/stat.c * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: (46 commits) userns: Silence silly gcc warning. cred: use correct cred accessor with regards to rcu read lock userns: Convert the move_pages, and migrate_pages permission checks to use uid_eq userns: Convert cgroup permission checks to use uid_eq userns: Convert tmpfs to use kuid and kgid where appropriate userns: Convert sysfs to use kgid/kuid where appropriate userns: Convert sysctl permission checks to use kuid and kgids. userns: Convert proc to use kuid/kgid where appropriate userns: Convert ext4 to user kuid/kgid where appropriate userns: Convert ext3 to use kuid/kgid where appropriate userns: Convert ext2 to use kuid/kgid where appropriate. userns: Convert devpts to use kuid/kgid where appropriate userns: Convert binary formats to use kuid/kgid where appropriate userns: Add negative depends on entries to avoid building code that is userns unsafe userns: signal remove unnecessary map_cred_ns userns: Teach inode_capable to understand inodes whose uids map to other namespaces. userns: Fail exec for suid and sgid binaries with ids outside our user namespace. userns: Convert stat to return values mapped from kuids and kgids userns: Convert user specfied uids and gids in chown into kuids and kgid userns: Use uid_eq gid_eq helpers when comparing kuids and kgids in the vfs ...
Diffstat (limited to 'fs/ext3')
-rw-r--r--fs/ext3/balloc.c5
-rw-r--r--fs/ext3/ext3.h8
-rw-r--r--fs/ext3/inode.c32
-rw-r--r--fs/ext3/super.c35
4 files changed, 52 insertions, 28 deletions
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index baac1b129fba..25cd60892116 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -1439,8 +1439,9 @@ static int ext3_has_free_blocks(struct ext3_sb_info *sbi, int use_reservation)
1439 free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter); 1439 free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
1440 root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count); 1440 root_blocks = le32_to_cpu(sbi->s_es->s_r_blocks_count);
1441 if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) && 1441 if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
1442 !use_reservation && sbi->s_resuid != current_fsuid() && 1442 !use_reservation && !uid_eq(sbi->s_resuid, current_fsuid()) &&
1443 (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) { 1443 (gid_eq(sbi->s_resgid, GLOBAL_ROOT_GID) ||
1444 !in_group_p (sbi->s_resgid))) {
1444 return 0; 1445 return 0;
1445 } 1446 }
1446 return 1; 1447 return 1;
diff --git a/fs/ext3/ext3.h b/fs/ext3/ext3.h
index b6515fd7e56c..7977973a24f0 100644
--- a/fs/ext3/ext3.h
+++ b/fs/ext3/ext3.h
@@ -243,8 +243,8 @@ struct ext3_new_group_data {
243 */ 243 */
244struct ext3_mount_options { 244struct ext3_mount_options {
245 unsigned long s_mount_opt; 245 unsigned long s_mount_opt;
246 uid_t s_resuid; 246 kuid_t s_resuid;
247 gid_t s_resgid; 247 kgid_t s_resgid;
248 unsigned long s_commit_interval; 248 unsigned long s_commit_interval;
249#ifdef CONFIG_QUOTA 249#ifdef CONFIG_QUOTA
250 int s_jquota_fmt; 250 int s_jquota_fmt;
@@ -637,8 +637,8 @@ struct ext3_sb_info {
637 struct buffer_head ** s_group_desc; 637 struct buffer_head ** s_group_desc;
638 unsigned long s_mount_opt; 638 unsigned long s_mount_opt;
639 ext3_fsblk_t s_sb_block; 639 ext3_fsblk_t s_sb_block;
640 uid_t s_resuid; 640 kuid_t s_resuid;
641 gid_t s_resgid; 641 kgid_t s_resgid;
642 unsigned short s_mount_state; 642 unsigned short s_mount_state;
643 unsigned short s_pad; 643 unsigned short s_pad;
644 int s_addr_per_block_bits; 644 int s_addr_per_block_bits;
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 10d7812f6021..a09790a412b1 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -2891,6 +2891,8 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino)
2891 transaction_t *transaction; 2891 transaction_t *transaction;
2892 long ret; 2892 long ret;
2893 int block; 2893 int block;
2894 uid_t i_uid;
2895 gid_t i_gid;
2894 2896
2895 inode = iget_locked(sb, ino); 2897 inode = iget_locked(sb, ino);
2896 if (!inode) 2898 if (!inode)
@@ -2907,12 +2909,14 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino)
2907 bh = iloc.bh; 2909 bh = iloc.bh;
2908 raw_inode = ext3_raw_inode(&iloc); 2910 raw_inode = ext3_raw_inode(&iloc);
2909 inode->i_mode = le16_to_cpu(raw_inode->i_mode); 2911 inode->i_mode = le16_to_cpu(raw_inode->i_mode);
2910 inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low); 2912 i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
2911 inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low); 2913 i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
2912 if(!(test_opt (inode->i_sb, NO_UID32))) { 2914 if(!(test_opt (inode->i_sb, NO_UID32))) {
2913 inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16; 2915 i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
2914 inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16; 2916 i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
2915 } 2917 }
2918 i_uid_write(inode, i_uid);
2919 i_gid_write(inode, i_gid);
2916 set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); 2920 set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
2917 inode->i_size = le32_to_cpu(raw_inode->i_size); 2921 inode->i_size = le32_to_cpu(raw_inode->i_size);
2918 inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime); 2922 inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);
@@ -3068,6 +3072,8 @@ static int ext3_do_update_inode(handle_t *handle,
3068 struct ext3_inode_info *ei = EXT3_I(inode); 3072 struct ext3_inode_info *ei = EXT3_I(inode);
3069 struct buffer_head *bh = iloc->bh; 3073 struct buffer_head *bh = iloc->bh;
3070 int err = 0, rc, block; 3074 int err = 0, rc, block;
3075 uid_t i_uid;
3076 gid_t i_gid;
3071 3077
3072again: 3078again:
3073 /* we can't allow multiple procs in here at once, its a bit racey */ 3079 /* we can't allow multiple procs in here at once, its a bit racey */
@@ -3080,27 +3086,29 @@ again:
3080 3086
3081 ext3_get_inode_flags(ei); 3087 ext3_get_inode_flags(ei);
3082 raw_inode->i_mode = cpu_to_le16(inode->i_mode); 3088 raw_inode->i_mode = cpu_to_le16(inode->i_mode);
3089 i_uid = i_uid_read(inode);
3090 i_gid = i_gid_read(inode);
3083 if(!(test_opt(inode->i_sb, NO_UID32))) { 3091 if(!(test_opt(inode->i_sb, NO_UID32))) {
3084 raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); 3092 raw_inode->i_uid_low = cpu_to_le16(low_16_bits(i_uid));
3085 raw_inode->i_gid_low = cpu_to_le16(low_16_bits(inode->i_gid)); 3093 raw_inode->i_gid_low = cpu_to_le16(low_16_bits(i_gid));
3086/* 3094/*
3087 * Fix up interoperability with old kernels. Otherwise, old inodes get 3095 * Fix up interoperability with old kernels. Otherwise, old inodes get
3088 * re-used with the upper 16 bits of the uid/gid intact 3096 * re-used with the upper 16 bits of the uid/gid intact
3089 */ 3097 */
3090 if(!ei->i_dtime) { 3098 if(!ei->i_dtime) {
3091 raw_inode->i_uid_high = 3099 raw_inode->i_uid_high =
3092 cpu_to_le16(high_16_bits(inode->i_uid)); 3100 cpu_to_le16(high_16_bits(i_uid));
3093 raw_inode->i_gid_high = 3101 raw_inode->i_gid_high =
3094 cpu_to_le16(high_16_bits(inode->i_gid)); 3102 cpu_to_le16(high_16_bits(i_gid));
3095 } else { 3103 } else {
3096 raw_inode->i_uid_high = 0; 3104 raw_inode->i_uid_high = 0;
3097 raw_inode->i_gid_high = 0; 3105 raw_inode->i_gid_high = 0;
3098 } 3106 }
3099 } else { 3107 } else {
3100 raw_inode->i_uid_low = 3108 raw_inode->i_uid_low =
3101 cpu_to_le16(fs_high2lowuid(inode->i_uid)); 3109 cpu_to_le16(fs_high2lowuid(i_uid));
3102 raw_inode->i_gid_low = 3110 raw_inode->i_gid_low =
3103 cpu_to_le16(fs_high2lowgid(inode->i_gid)); 3111 cpu_to_le16(fs_high2lowgid(i_gid));
3104 raw_inode->i_uid_high = 0; 3112 raw_inode->i_uid_high = 0;
3105 raw_inode->i_gid_high = 0; 3113 raw_inode->i_gid_high = 0;
3106 } 3114 }
@@ -3262,8 +3270,8 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
3262 3270
3263 if (is_quota_modification(inode, attr)) 3271 if (is_quota_modification(inode, attr))
3264 dquot_initialize(inode); 3272 dquot_initialize(inode);
3265 if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || 3273 if ((ia_valid & ATTR_UID && !uid_eq(attr->ia_uid, inode->i_uid)) ||
3266 (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { 3274 (ia_valid & ATTR_GID && !gid_eq(attr->ia_gid, inode->i_gid))) {
3267 handle_t *handle; 3275 handle_t *handle;
3268 3276
3269 /* (user+group)*(old+new) structure, inode write (sb, 3277 /* (user+group)*(old+new) structure, inode write (sb,
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index cf0b5921cf0f..94ef7e616129 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -617,13 +617,15 @@ static int ext3_show_options(struct seq_file *seq, struct dentry *root)
617 seq_puts(seq, ",grpid"); 617 seq_puts(seq, ",grpid");
618 if (!test_opt(sb, GRPID) && (def_mount_opts & EXT3_DEFM_BSDGROUPS)) 618 if (!test_opt(sb, GRPID) && (def_mount_opts & EXT3_DEFM_BSDGROUPS))
619 seq_puts(seq, ",nogrpid"); 619 seq_puts(seq, ",nogrpid");
620 if (sbi->s_resuid != EXT3_DEF_RESUID || 620 if (!uid_eq(sbi->s_resuid, make_kuid(&init_user_ns, EXT3_DEF_RESUID)) ||
621 le16_to_cpu(es->s_def_resuid) != EXT3_DEF_RESUID) { 621 le16_to_cpu(es->s_def_resuid) != EXT3_DEF_RESUID) {
622 seq_printf(seq, ",resuid=%u", sbi->s_resuid); 622 seq_printf(seq, ",resuid=%u",
623 from_kuid_munged(&init_user_ns, sbi->s_resuid));
623 } 624 }
624 if (sbi->s_resgid != EXT3_DEF_RESGID || 625 if (!gid_eq(sbi->s_resgid, make_kgid(&init_user_ns, EXT3_DEF_RESGID)) ||
625 le16_to_cpu(es->s_def_resgid) != EXT3_DEF_RESGID) { 626 le16_to_cpu(es->s_def_resgid) != EXT3_DEF_RESGID) {
626 seq_printf(seq, ",resgid=%u", sbi->s_resgid); 627 seq_printf(seq, ",resgid=%u",
628 from_kgid_munged(&init_user_ns, sbi->s_resgid));
627 } 629 }
628 if (test_opt(sb, ERRORS_RO)) { 630 if (test_opt(sb, ERRORS_RO)) {
629 int def_errors = le16_to_cpu(es->s_errors); 631 int def_errors = le16_to_cpu(es->s_errors);
@@ -967,6 +969,8 @@ static int parse_options (char *options, struct super_block *sb,
967 substring_t args[MAX_OPT_ARGS]; 969 substring_t args[MAX_OPT_ARGS];
968 int data_opt = 0; 970 int data_opt = 0;
969 int option; 971 int option;
972 kuid_t uid;
973 kgid_t gid;
970#ifdef CONFIG_QUOTA 974#ifdef CONFIG_QUOTA
971 int qfmt; 975 int qfmt;
972#endif 976#endif
@@ -1000,12 +1004,23 @@ static int parse_options (char *options, struct super_block *sb,
1000 case Opt_resuid: 1004 case Opt_resuid:
1001 if (match_int(&args[0], &option)) 1005 if (match_int(&args[0], &option))
1002 return 0; 1006 return 0;
1003 sbi->s_resuid = option; 1007 uid = make_kuid(current_user_ns(), option);
1008 if (!uid_valid(uid)) {
1009 ext3_msg(sb, KERN_ERR, "Invalid uid value %d", option);
1010 return -1;
1011
1012 }
1013 sbi->s_resuid = uid;
1004 break; 1014 break;
1005 case Opt_resgid: 1015 case Opt_resgid:
1006 if (match_int(&args[0], &option)) 1016 if (match_int(&args[0], &option))
1007 return 0; 1017 return 0;
1008 sbi->s_resgid = option; 1018 gid = make_kgid(current_user_ns(), option);
1019 if (!gid_valid(gid)) {
1020 ext3_msg(sb, KERN_ERR, "Invalid gid value %d", option);
1021 return -1;
1022 }
1023 sbi->s_resgid = gid;
1009 break; 1024 break;
1010 case Opt_sb: 1025 case Opt_sb:
1011 /* handled by get_sb_block() instead of here */ 1026 /* handled by get_sb_block() instead of here */
@@ -1651,8 +1666,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
1651 } 1666 }
1652 sb->s_fs_info = sbi; 1667 sb->s_fs_info = sbi;
1653 sbi->s_mount_opt = 0; 1668 sbi->s_mount_opt = 0;
1654 sbi->s_resuid = EXT3_DEF_RESUID; 1669 sbi->s_resuid = make_kuid(&init_user_ns, EXT3_DEF_RESUID);
1655 sbi->s_resgid = EXT3_DEF_RESGID; 1670 sbi->s_resgid = make_kgid(&init_user_ns, EXT3_DEF_RESGID);
1656 sbi->s_sb_block = sb_block; 1671 sbi->s_sb_block = sb_block;
1657 1672
1658 blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE); 1673 blocksize = sb_min_blocksize(sb, EXT3_MIN_BLOCK_SIZE);
@@ -1716,8 +1731,8 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent)
1716 else 1731 else
1717 set_opt(sbi->s_mount_opt, ERRORS_RO); 1732 set_opt(sbi->s_mount_opt, ERRORS_RO);
1718 1733
1719 sbi->s_resuid = le16_to_cpu(es->s_def_resuid); 1734 sbi->s_resuid = make_kuid(&init_user_ns, le16_to_cpu(es->s_def_resuid));
1720 sbi->s_resgid = le16_to_cpu(es->s_def_resgid); 1735 sbi->s_resgid = make_kgid(&init_user_ns, le16_to_cpu(es->s_def_resgid));
1721 1736
1722 /* enable barriers by default */ 1737 /* enable barriers by default */
1723 set_opt(sbi->s_mount_opt, BARRIER); 1738 set_opt(sbi->s_mount_opt, BARRIER);