diff options
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 99 |
1 files changed, 47 insertions, 52 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 419f746d851d..7bb2cda3bfef 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -2186,13 +2186,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, | |||
2186 | } | 2186 | } |
2187 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && | 2187 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && |
2188 | !(mnt_flags & MNT_NODEV)) { | 2188 | !(mnt_flags & MNT_NODEV)) { |
2189 | /* Was the nodev implicitly added in mount? */ | 2189 | return -EPERM; |
2190 | if ((mnt->mnt_ns->user_ns != &init_user_ns) && | ||
2191 | !(sb->s_type->fs_flags & FS_USERNS_DEV_MOUNT)) { | ||
2192 | mnt_flags |= MNT_NODEV; | ||
2193 | } else { | ||
2194 | return -EPERM; | ||
2195 | } | ||
2196 | } | 2190 | } |
2197 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) && | 2191 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) && |
2198 | !(mnt_flags & MNT_NOSUID)) { | 2192 | !(mnt_flags & MNT_NOSUID)) { |
@@ -2376,7 +2370,7 @@ unlock: | |||
2376 | return err; | 2370 | return err; |
2377 | } | 2371 | } |
2378 | 2372 | ||
2379 | static bool fs_fully_visible(struct file_system_type *fs_type, int *new_mnt_flags); | 2373 | static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags); |
2380 | 2374 | ||
2381 | /* | 2375 | /* |
2382 | * create a new mount for userspace and request it to be added into the | 2376 | * create a new mount for userspace and request it to be added into the |
@@ -2386,7 +2380,6 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
2386 | int mnt_flags, const char *name, void *data) | 2380 | int mnt_flags, const char *name, void *data) |
2387 | { | 2381 | { |
2388 | struct file_system_type *type; | 2382 | struct file_system_type *type; |
2389 | struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns; | ||
2390 | struct vfsmount *mnt; | 2383 | struct vfsmount *mnt; |
2391 | int err; | 2384 | int err; |
2392 | 2385 | ||
@@ -2397,26 +2390,6 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
2397 | if (!type) | 2390 | if (!type) |
2398 | return -ENODEV; | 2391 | return -ENODEV; |
2399 | 2392 | ||
2400 | if (user_ns != &init_user_ns) { | ||
2401 | if (!(type->fs_flags & FS_USERNS_MOUNT)) { | ||
2402 | put_filesystem(type); | ||
2403 | return -EPERM; | ||
2404 | } | ||
2405 | /* Only in special cases allow devices from mounts | ||
2406 | * created outside the initial user namespace. | ||
2407 | */ | ||
2408 | if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) { | ||
2409 | flags |= MS_NODEV; | ||
2410 | mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV; | ||
2411 | } | ||
2412 | if (type->fs_flags & FS_USERNS_VISIBLE) { | ||
2413 | if (!fs_fully_visible(type, &mnt_flags)) { | ||
2414 | put_filesystem(type); | ||
2415 | return -EPERM; | ||
2416 | } | ||
2417 | } | ||
2418 | } | ||
2419 | |||
2420 | mnt = vfs_kern_mount(type, flags, name, data); | 2393 | mnt = vfs_kern_mount(type, flags, name, data); |
2421 | if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && | 2394 | if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) && |
2422 | !mnt->mnt_sb->s_subtype) | 2395 | !mnt->mnt_sb->s_subtype) |
@@ -2426,6 +2399,11 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
2426 | if (IS_ERR(mnt)) | 2399 | if (IS_ERR(mnt)) |
2427 | return PTR_ERR(mnt); | 2400 | return PTR_ERR(mnt); |
2428 | 2401 | ||
2402 | if (mount_too_revealing(mnt, &mnt_flags)) { | ||
2403 | mntput(mnt); | ||
2404 | return -EPERM; | ||
2405 | } | ||
2406 | |||
2429 | err = do_add_mount(real_mount(mnt), path, mnt_flags); | 2407 | err = do_add_mount(real_mount(mnt), path, mnt_flags); |
2430 | if (err) | 2408 | if (err) |
2431 | mntput(mnt); | 2409 | mntput(mnt); |
@@ -3217,22 +3195,19 @@ bool current_chrooted(void) | |||
3217 | return chrooted; | 3195 | return chrooted; |
3218 | } | 3196 | } |
3219 | 3197 | ||
3220 | static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) | 3198 | static bool mnt_already_visible(struct mnt_namespace *ns, struct vfsmount *new, |
3199 | int *new_mnt_flags) | ||
3221 | { | 3200 | { |
3222 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
3223 | int new_flags = *new_mnt_flags; | 3201 | int new_flags = *new_mnt_flags; |
3224 | struct mount *mnt; | 3202 | struct mount *mnt; |
3225 | bool visible = false; | 3203 | bool visible = false; |
3226 | 3204 | ||
3227 | if (unlikely(!ns)) | ||
3228 | return false; | ||
3229 | |||
3230 | down_read(&namespace_sem); | 3205 | down_read(&namespace_sem); |
3231 | list_for_each_entry(mnt, &ns->list, mnt_list) { | 3206 | list_for_each_entry(mnt, &ns->list, mnt_list) { |
3232 | struct mount *child; | 3207 | struct mount *child; |
3233 | int mnt_flags; | 3208 | int mnt_flags; |
3234 | 3209 | ||
3235 | if (mnt->mnt.mnt_sb->s_type != type) | 3210 | if (mnt->mnt.mnt_sb->s_type != new->mnt_sb->s_type) |
3236 | continue; | 3211 | continue; |
3237 | 3212 | ||
3238 | /* This mount is not fully visible if it's root directory | 3213 | /* This mount is not fully visible if it's root directory |
@@ -3241,12 +3216,8 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) | |||
3241 | if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root) | 3216 | if (mnt->mnt.mnt_root != mnt->mnt.mnt_sb->s_root) |
3242 | continue; | 3217 | continue; |
3243 | 3218 | ||
3244 | /* Read the mount flags and filter out flags that | 3219 | /* A local view of the mount flags */ |
3245 | * may safely be ignored. | ||
3246 | */ | ||
3247 | mnt_flags = mnt->mnt.mnt_flags; | 3220 | mnt_flags = mnt->mnt.mnt_flags; |
3248 | if (mnt->mnt.mnt_sb->s_iflags & SB_I_NOEXEC) | ||
3249 | mnt_flags &= ~(MNT_LOCK_NOSUID | MNT_LOCK_NOEXEC); | ||
3250 | 3221 | ||
3251 | /* Don't miss readonly hidden in the superblock flags */ | 3222 | /* Don't miss readonly hidden in the superblock flags */ |
3252 | if (mnt->mnt.mnt_sb->s_flags & MS_RDONLY) | 3223 | if (mnt->mnt.mnt_sb->s_flags & MS_RDONLY) |
@@ -3258,15 +3229,6 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) | |||
3258 | if ((mnt_flags & MNT_LOCK_READONLY) && | 3229 | if ((mnt_flags & MNT_LOCK_READONLY) && |
3259 | !(new_flags & MNT_READONLY)) | 3230 | !(new_flags & MNT_READONLY)) |
3260 | continue; | 3231 | continue; |
3261 | if ((mnt_flags & MNT_LOCK_NODEV) && | ||
3262 | !(new_flags & MNT_NODEV)) | ||
3263 | continue; | ||
3264 | if ((mnt_flags & MNT_LOCK_NOSUID) && | ||
3265 | !(new_flags & MNT_NOSUID)) | ||
3266 | continue; | ||
3267 | if ((mnt_flags & MNT_LOCK_NOEXEC) && | ||
3268 | !(new_flags & MNT_NOEXEC)) | ||
3269 | continue; | ||
3270 | if ((mnt_flags & MNT_LOCK_ATIME) && | 3232 | if ((mnt_flags & MNT_LOCK_ATIME) && |
3271 | ((mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK))) | 3233 | ((mnt_flags & MNT_ATIME_MASK) != (new_flags & MNT_ATIME_MASK))) |
3272 | continue; | 3234 | continue; |
@@ -3286,9 +3248,6 @@ static bool fs_fully_visible(struct file_system_type *type, int *new_mnt_flags) | |||
3286 | } | 3248 | } |
3287 | /* Preserve the locked attributes */ | 3249 | /* Preserve the locked attributes */ |
3288 | *new_mnt_flags |= mnt_flags & (MNT_LOCK_READONLY | \ | 3250 | *new_mnt_flags |= mnt_flags & (MNT_LOCK_READONLY | \ |
3289 | MNT_LOCK_NODEV | \ | ||
3290 | MNT_LOCK_NOSUID | \ | ||
3291 | MNT_LOCK_NOEXEC | \ | ||
3292 | MNT_LOCK_ATIME); | 3251 | MNT_LOCK_ATIME); |
3293 | visible = true; | 3252 | visible = true; |
3294 | goto found; | 3253 | goto found; |
@@ -3299,6 +3258,42 @@ found: | |||
3299 | return visible; | 3258 | return visible; |
3300 | } | 3259 | } |
3301 | 3260 | ||
3261 | static bool mount_too_revealing(struct vfsmount *mnt, int *new_mnt_flags) | ||
3262 | { | ||
3263 | const unsigned long required_iflags = SB_I_NOEXEC | SB_I_NODEV; | ||
3264 | struct mnt_namespace *ns = current->nsproxy->mnt_ns; | ||
3265 | unsigned long s_iflags; | ||
3266 | |||
3267 | if (ns->user_ns == &init_user_ns) | ||
3268 | return false; | ||
3269 | |||
3270 | /* Can this filesystem be too revealing? */ | ||
3271 | s_iflags = mnt->mnt_sb->s_iflags; | ||
3272 | if (!(s_iflags & SB_I_USERNS_VISIBLE)) | ||
3273 | return false; | ||
3274 | |||
3275 | if ((s_iflags & required_iflags) != required_iflags) { | ||
3276 | WARN_ONCE(1, "Expected s_iflags to contain 0x%lx\n", | ||
3277 | required_iflags); | ||
3278 | return true; | ||
3279 | } | ||
3280 | |||
3281 | return !mnt_already_visible(ns, mnt, new_mnt_flags); | ||
3282 | } | ||
3283 | |||
3284 | bool mnt_may_suid(struct vfsmount *mnt) | ||
3285 | { | ||
3286 | /* | ||
3287 | * Foreign mounts (accessed via fchdir or through /proc | ||
3288 | * symlinks) are always treated as if they are nosuid. This | ||
3289 | * prevents namespaces from trusting potentially unsafe | ||
3290 | * suid/sgid bits, file caps, or security labels that originate | ||
3291 | * in other namespaces. | ||
3292 | */ | ||
3293 | return !(mnt->mnt_flags & MNT_NOSUID) && check_mnt(real_mount(mnt)) && | ||
3294 | current_in_userns(mnt->mnt_sb->s_user_ns); | ||
3295 | } | ||
3296 | |||
3302 | static struct ns_common *mntns_get(struct task_struct *task) | 3297 | static struct ns_common *mntns_get(struct task_struct *task) |
3303 | { | 3298 | { |
3304 | struct ns_common *ns = NULL; | 3299 | struct ns_common *ns = NULL; |