diff options
Diffstat (limited to 'security/tomoyo/mount.c')
| -rw-r--r-- | security/tomoyo/mount.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c new file mode 100644 index 00000000000..82bf8c2390b --- /dev/null +++ b/security/tomoyo/mount.c | |||
| @@ -0,0 +1,284 @@ | |||
| 1 | /* | ||
| 2 | * security/tomoyo/mount.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2010 NTT DATA CORPORATION | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/slab.h> | ||
| 8 | #include "common.h" | ||
| 9 | |||
| 10 | /* Keywords for mount restrictions. */ | ||
| 11 | |||
| 12 | /* Allow to call 'mount --bind /source_dir /dest_dir' */ | ||
| 13 | #define TOMOYO_MOUNT_BIND_KEYWORD "--bind" | ||
| 14 | /* Allow to call 'mount --move /old_dir /new_dir ' */ | ||
| 15 | #define TOMOYO_MOUNT_MOVE_KEYWORD "--move" | ||
| 16 | /* Allow to call 'mount -o remount /dir ' */ | ||
| 17 | #define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount" | ||
| 18 | /* Allow to call 'mount --make-unbindable /dir' */ | ||
| 19 | #define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" | ||
| 20 | /* Allow to call 'mount --make-private /dir' */ | ||
| 21 | #define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" | ||
| 22 | /* Allow to call 'mount --make-slave /dir' */ | ||
| 23 | #define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" | ||
| 24 | /* Allow to call 'mount --make-shared /dir' */ | ||
| 25 | #define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" | ||
| 26 | |||
| 27 | /** | ||
| 28 | * tomoyo_audit_mount_log - Audit mount log. | ||
| 29 | * | ||
| 30 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 31 | * | ||
| 32 | * Returns 0 on success, negative value otherwise. | ||
| 33 | */ | ||
| 34 | static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) | ||
| 35 | { | ||
| 36 | const char *dev = r->param.mount.dev->name; | ||
| 37 | const char *dir = r->param.mount.dir->name; | ||
| 38 | const char *type = r->param.mount.type->name; | ||
| 39 | const unsigned long flags = r->param.mount.flags; | ||
| 40 | if (r->granted) | ||
| 41 | return 0; | ||
| 42 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) | ||
| 43 | tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags); | ||
| 44 | else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) | ||
| 45 | || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) | ||
| 46 | tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir, | ||
| 47 | flags); | ||
| 48 | else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
| 49 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
| 50 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
| 51 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) | ||
| 52 | tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags); | ||
| 53 | else | ||
| 54 | tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir, | ||
| 55 | flags); | ||
| 56 | return tomoyo_supervisor(r, | ||
| 57 | TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", | ||
| 58 | tomoyo_pattern(r->param.mount.dev), | ||
| 59 | tomoyo_pattern(r->param.mount.dir), type, | ||
| 60 | flags); | ||
| 61 | } | ||
| 62 | |||
| 63 | static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, | ||
| 64 | const struct tomoyo_acl_info *ptr) | ||
| 65 | { | ||
| 66 | const struct tomoyo_mount_acl *acl = | ||
| 67 | container_of(ptr, typeof(*acl), head); | ||
| 68 | return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) && | ||
| 69 | tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) && | ||
| 70 | tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) && | ||
| 71 | (!r->param.mount.need_dev || | ||
| 72 | tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name)); | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * tomoyo_mount_acl - Check permission for mount() operation. | ||
| 77 | * | ||
| 78 | * @r: Pointer to "struct tomoyo_request_info". | ||
| 79 | * @dev_name: Name of device file. | ||
| 80 | * @dir: Pointer to "struct path". | ||
| 81 | * @type: Name of filesystem type. | ||
| 82 | * @flags: Mount options. | ||
| 83 | * | ||
| 84 | * Returns 0 on success, negative value otherwise. | ||
| 85 | * | ||
| 86 | * Caller holds tomoyo_read_lock(). | ||
| 87 | */ | ||
| 88 | static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, | ||
| 89 | struct path *dir, char *type, unsigned long flags) | ||
| 90 | { | ||
| 91 | struct path path; | ||
| 92 | struct file_system_type *fstype = NULL; | ||
| 93 | const char *requested_type = NULL; | ||
| 94 | const char *requested_dir_name = NULL; | ||
| 95 | const char *requested_dev_name = NULL; | ||
| 96 | struct tomoyo_path_info rtype; | ||
| 97 | struct tomoyo_path_info rdev; | ||
| 98 | struct tomoyo_path_info rdir; | ||
| 99 | int need_dev = 0; | ||
| 100 | int error = -ENOMEM; | ||
| 101 | |||
| 102 | /* Get fstype. */ | ||
| 103 | requested_type = tomoyo_encode(type); | ||
| 104 | if (!requested_type) | ||
| 105 | goto out; | ||
| 106 | rtype.name = requested_type; | ||
| 107 | tomoyo_fill_path_info(&rtype); | ||
| 108 | |||
| 109 | /* Get mount point. */ | ||
| 110 | requested_dir_name = tomoyo_realpath_from_path(dir); | ||
| 111 | if (!requested_dir_name) { | ||
| 112 | error = -ENOMEM; | ||
| 113 | goto out; | ||
| 114 | } | ||
| 115 | rdir.name = requested_dir_name; | ||
| 116 | tomoyo_fill_path_info(&rdir); | ||
| 117 | |||
| 118 | /* Compare fs name. */ | ||
| 119 | if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { | ||
| 120 | /* dev_name is ignored. */ | ||
| 121 | } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || | ||
| 122 | !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || | ||
| 123 | !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || | ||
| 124 | !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { | ||
| 125 | /* dev_name is ignored. */ | ||
| 126 | } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || | ||
| 127 | !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { | ||
| 128 | need_dev = -1; /* dev_name is a directory */ | ||
| 129 | } else { | ||
| 130 | fstype = get_fs_type(type); | ||
| 131 | if (!fstype) { | ||
| 132 | error = -ENODEV; | ||
| 133 | goto out; | ||
| 134 | } | ||
| 135 | if (fstype->fs_flags & FS_REQUIRES_DEV) | ||
| 136 | /* dev_name is a block device file. */ | ||
| 137 | need_dev = 1; | ||
| 138 | } | ||
| 139 | if (need_dev) { | ||
| 140 | /* Get mount point or device file. */ | ||
| 141 | if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { | ||
| 142 | error = -ENOENT; | ||
| 143 | goto out; | ||
| 144 | } | ||
| 145 | requested_dev_name = tomoyo_realpath_from_path(&path); | ||
| 146 | if (!requested_dev_name) { | ||
| 147 | error = -ENOENT; | ||
| 148 | goto out; | ||
| 149 | } | ||
| 150 | } else { | ||
| 151 | /* Map dev_name to "<NULL>" if no dev_name given. */ | ||
| 152 | if (!dev_name) | ||
| 153 | dev_name = "<NULL>"; | ||
| 154 | requested_dev_name = tomoyo_encode(dev_name); | ||
| 155 | if (!requested_dev_name) { | ||
| 156 | error = -ENOMEM; | ||
| 157 | goto out; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | rdev.name = requested_dev_name; | ||
| 161 | tomoyo_fill_path_info(&rdev); | ||
| 162 | r->param_type = TOMOYO_TYPE_MOUNT_ACL; | ||
| 163 | r->param.mount.need_dev = need_dev; | ||
| 164 | r->param.mount.dev = &rdev; | ||
| 165 | r->param.mount.dir = &rdir; | ||
| 166 | r->param.mount.type = &rtype; | ||
| 167 | r->param.mount.flags = flags; | ||
| 168 | do { | ||
| 169 | tomoyo_check_acl(r, tomoyo_check_mount_acl); | ||
| 170 | error = tomoyo_audit_mount_log(r); | ||
| 171 | } while (error == TOMOYO_RETRY_REQUEST); | ||
| 172 | out: | ||
| 173 | kfree(requested_dev_name); | ||
| 174 | kfree(requested_dir_name); | ||
| 175 | if (fstype) | ||
| 176 | put_filesystem(fstype); | ||
| 177 | kfree(requested_type); | ||
| 178 | return error; | ||
| 179 | } | ||
| 180 | |||
| 181 | /** | ||
| 182 | * tomoyo_mount_permission - Check permission for mount() operation. | ||
| 183 | * | ||
| 184 | * @dev_name: Name of device file. | ||
| 185 | * @path: Pointer to "struct path". | ||
| 186 | * @type: Name of filesystem type. May be NULL. | ||
| 187 | * @flags: Mount options. | ||
| 188 | * @data_page: Optional data. May be NULL. | ||
| 189 | * | ||
| 190 | * Returns 0 on success, negative value otherwise. | ||
| 191 | */ | ||
| 192 | int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, | ||
| 193 | unsigned long flags, void *data_page) | ||
| 194 | { | ||
| 195 | struct tomoyo_request_info r; | ||
| 196 | int error; | ||
| 197 | int idx; | ||
| 198 | |||
| 199 | if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) | ||
| 200 | == TOMOYO_CONFIG_DISABLED) | ||
| 201 | return 0; | ||
| 202 | if ((flags & MS_MGC_MSK) == MS_MGC_VAL) | ||
| 203 | flags &= ~MS_MGC_MSK; | ||
| 204 | if (flags & MS_REMOUNT) { | ||
| 205 | type = TOMOYO_MOUNT_REMOUNT_KEYWORD; | ||
| 206 | flags &= ~MS_REMOUNT; | ||
| 207 | } | ||
| 208 | if (flags & MS_MOVE) { | ||
| 209 | type = TOMOYO_MOUNT_MOVE_KEYWORD; | ||
| 210 | flags &= ~MS_MOVE; | ||
| 211 | } | ||
| 212 | if (flags & MS_BIND) { | ||
| 213 | type = TOMOYO_MOUNT_BIND_KEYWORD; | ||
| 214 | flags &= ~MS_BIND; | ||
| 215 | } | ||
| 216 | if (flags & MS_UNBINDABLE) { | ||
| 217 | type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD; | ||
| 218 | flags &= ~MS_UNBINDABLE; | ||
| 219 | } | ||
| 220 | if (flags & MS_PRIVATE) { | ||
| 221 | type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD; | ||
| 222 | flags &= ~MS_PRIVATE; | ||
| 223 | } | ||
| 224 | if (flags & MS_SLAVE) { | ||
| 225 | type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD; | ||
| 226 | flags &= ~MS_SLAVE; | ||
| 227 | } | ||
| 228 | if (flags & MS_SHARED) { | ||
| 229 | type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD; | ||
| 230 | flags &= ~MS_SHARED; | ||
| 231 | } | ||
| 232 | if (!type) | ||
| 233 | type = "<NULL>"; | ||
| 234 | idx = tomoyo_read_lock(); | ||
| 235 | error = tomoyo_mount_acl(&r, dev_name, path, type, flags); | ||
| 236 | tomoyo_read_unlock(idx); | ||
| 237 | return error; | ||
| 238 | } | ||
| 239 | |||
| 240 | static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, | ||
| 241 | const struct tomoyo_acl_info *b) | ||
| 242 | { | ||
| 243 | const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); | ||
| 244 | const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); | ||
| 245 | return tomoyo_same_acl_head(&p1->head, &p2->head) && | ||
| 246 | tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && | ||
| 247 | tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && | ||
| 248 | tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && | ||
| 249 | tomoyo_same_number_union(&p1->flags, &p2->flags); | ||
| 250 | } | ||
| 251 | |||
| 252 | /** | ||
| 253 | * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list. | ||
| 254 | * | ||
| 255 | * @data: String to parse. | ||
| 256 | * @domain: Pointer to "struct tomoyo_domain_info". | ||
| 257 | * @is_delete: True if it is a delete request. | ||
| 258 | * | ||
| 259 | * Returns 0 on success, negative value otherwise. | ||
| 260 | * | ||
| 261 | * Caller holds tomoyo_read_lock(). | ||
| 262 | */ | ||
| 263 | int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, | ||
| 264 | const bool is_delete) | ||
| 265 | { | ||
| 266 | struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; | ||
| 267 | int error = is_delete ? -ENOENT : -ENOMEM; | ||
| 268 | char *w[4]; | ||
| 269 | if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) | ||
| 270 | return -EINVAL; | ||
| 271 | if (!tomoyo_parse_name_union(w[0], &e.dev_name) || | ||
| 272 | !tomoyo_parse_name_union(w[1], &e.dir_name) || | ||
| 273 | !tomoyo_parse_name_union(w[2], &e.fs_type) || | ||
| 274 | !tomoyo_parse_number_union(w[3], &e.flags)) | ||
| 275 | goto out; | ||
| 276 | error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, | ||
| 277 | tomoyo_same_mount_acl, NULL); | ||
| 278 | out: | ||
| 279 | tomoyo_put_name_union(&e.dev_name); | ||
| 280 | tomoyo_put_name_union(&e.dir_name); | ||
| 281 | tomoyo_put_name_union(&e.fs_type); | ||
| 282 | tomoyo_put_number_union(&e.flags); | ||
| 283 | return error; | ||
| 284 | } | ||
