mount.c revision 17fcfbd9d45b57f38d40e31f9d28db53f4af5c88
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_encode2: Encode binary string to ascii string. 29 * 30 * @str: String in binary format. 31 * 32 * Returns pointer to @str in ascii format on success, NULL otherwise. 33 * 34 * This function uses kzalloc(), so caller must kfree() if this function 35 * didn't return NULL. 36 */ 37static char *tomoyo_encode2(const char *str) 38{ 39 int len = 0; 40 const char *p = str; 41 char *cp; 42 char *cp0; 43 if (!p) 44 return NULL; 45 while (*p) { 46 const unsigned char c = *p++; 47 if (c == '\\') 48 len += 2; 49 else if (c > ' ' && c < 127) 50 len++; 51 else 52 len += 4; 53 } 54 len++; 55 /* Reserve space for appending "/". */ 56 cp = kzalloc(len + 10, GFP_NOFS); 57 if (!cp) 58 return NULL; 59 cp0 = cp; 60 p = str; 61 while (*p) { 62 const unsigned char c = *p++; 63 if (c == '\\') { 64 *cp++ = '\\'; 65 *cp++ = '\\'; 66 } else if (c > ' ' && c < 127) { 67 *cp++ = c; 68 } else { 69 *cp++ = '\\'; 70 *cp++ = (c >> 6) + '0'; 71 *cp++ = ((c >> 3) & 7) + '0'; 72 *cp++ = (c & 7) + '0'; 73 } 74 } 75 return cp0; 76} 77 78/** 79 * tomoyo_mount_acl2 - Check permission for mount() operation. 80 * 81 * @r: Pointer to "struct tomoyo_request_info". 82 * @dev_name: Name of device file. 83 * @dir: Pointer to "struct path". 84 * @type: Name of filesystem type. 85 * @flags: Mount options. 86 * 87 * Returns 0 on success, negative value otherwise. 88 * 89 * Caller holds tomoyo_read_lock(). 90 */ 91static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, 92 struct path *dir, char *type, unsigned long flags) 93{ 94 struct path path; 95 struct tomoyo_acl_info *ptr; 96 struct file_system_type *fstype = NULL; 97 const char *requested_type = NULL; 98 const char *requested_dir_name = NULL; 99 const char *requested_dev_name = NULL; 100 struct tomoyo_path_info rtype; 101 struct tomoyo_path_info rdev; 102 struct tomoyo_path_info rdir; 103 int need_dev = 0; 104 int error = -ENOMEM; 105 106 /* Get fstype. */ 107 requested_type = tomoyo_encode2(type); 108 if (!requested_type) 109 goto out; 110 rtype.name = requested_type; 111 tomoyo_fill_path_info(&rtype); 112 113 /* Get mount point. */ 114 requested_dir_name = tomoyo_realpath_from_path(dir); 115 if (!requested_dir_name) { 116 error = -ENOMEM; 117 goto out; 118 } 119 rdir.name = requested_dir_name; 120 tomoyo_fill_path_info(&rdir); 121 122 /* Compare fs name. */ 123 if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { 124 /* dev_name is ignored. */ 125 } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || 126 !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || 127 !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || 128 !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { 129 /* dev_name is ignored. */ 130 } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || 131 !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { 132 need_dev = -1; /* dev_name is a directory */ 133 } else { 134 fstype = get_fs_type(type); 135 if (!fstype) { 136 error = -ENODEV; 137 goto out; 138 } 139 if (fstype->fs_flags & FS_REQUIRES_DEV) 140 /* dev_name is a block device file. */ 141 need_dev = 1; 142 } 143 if (need_dev) { 144 /* Get mount point or device file. */ 145 if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { 146 error = -ENOENT; 147 goto out; 148 } 149 requested_dev_name = tomoyo_realpath_from_path(&path); 150 if (!requested_dev_name) { 151 error = -ENOENT; 152 goto out; 153 } 154 } else { 155 /* Map dev_name to "<NULL>" if no dev_name given. */ 156 if (!dev_name) 157 dev_name = "<NULL>"; 158 requested_dev_name = tomoyo_encode2(dev_name); 159 if (!requested_dev_name) { 160 error = -ENOMEM; 161 goto out; 162 } 163 } 164 rdev.name = requested_dev_name; 165 tomoyo_fill_path_info(&rdev); 166 list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { 167 struct tomoyo_mount_acl *acl; 168 if (ptr->type != TOMOYO_TYPE_MOUNT_ACL) 169 continue; 170 acl = container_of(ptr, struct tomoyo_mount_acl, head); 171 if (acl->is_deleted || 172 !tomoyo_compare_number_union(flags, &acl->flags) || 173 !tomoyo_compare_name_union(&rtype, &acl->fs_type) || 174 !tomoyo_compare_name_union(&rdir, &acl->dir_name) || 175 (need_dev && 176 !tomoyo_compare_name_union(&rdev, &acl->dev_name))) 177 continue; 178 error = 0; 179 break; 180 } 181 if (error) 182 error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT 183 "%s %s %s 0x%lX\n", 184 tomoyo_file_pattern(&rdev), 185 tomoyo_file_pattern(&rdir), 186 requested_type, flags); 187 out: 188 kfree(requested_dev_name); 189 kfree(requested_dir_name); 190 if (fstype) 191 put_filesystem(fstype); 192 kfree(requested_type); 193 return error; 194} 195 196/** 197 * tomoyo_mount_acl - Check permission for mount() operation. 198 * 199 * @r: Pointer to "struct tomoyo_request_info". 200 * @dev_name: Name of device file. 201 * @dir: Pointer to "struct path". 202 * @type: Name of filesystem type. 203 * @flags: Mount options. 204 * 205 * Returns 0 on success, negative value otherwise. 206 * 207 * Caller holds tomoyo_read_lock(). 208 */ 209static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, 210 struct path *dir, char *type, unsigned long flags) 211{ 212 int error; 213 error = -EPERM; 214 if ((flags & MS_MGC_MSK) == MS_MGC_VAL) 215 flags &= ~MS_MGC_MSK; 216 switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { 217 case MS_REMOUNT: 218 case MS_MOVE: 219 case MS_BIND: 220 case 0: 221 break; 222 default: 223 printk(KERN_WARNING "ERROR: " 224 "%s%s%sare given for single mount operation.\n", 225 flags & MS_REMOUNT ? "'remount' " : "", 226 flags & MS_MOVE ? "'move' " : "", 227 flags & MS_BIND ? "'bind' " : ""); 228 return -EINVAL; 229 } 230 switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { 231 case MS_UNBINDABLE: 232 case MS_PRIVATE: 233 case MS_SLAVE: 234 case MS_SHARED: 235 case 0: 236 break; 237 default: 238 printk(KERN_WARNING "ERROR: " 239 "%s%s%s%sare given for single mount operation.\n", 240 flags & MS_UNBINDABLE ? "'unbindable' " : "", 241 flags & MS_PRIVATE ? "'private' " : "", 242 flags & MS_SLAVE ? "'slave' " : "", 243 flags & MS_SHARED ? "'shared' " : ""); 244 return -EINVAL; 245 } 246 if (flags & MS_REMOUNT) 247 error = tomoyo_mount_acl(r, dev_name, dir, 248 TOMOYO_MOUNT_REMOUNT_KEYWORD, 249 flags & ~MS_REMOUNT); 250 else if (flags & MS_MOVE) 251 error = tomoyo_mount_acl(r, dev_name, dir, 252 TOMOYO_MOUNT_MOVE_KEYWORD, 253 flags & ~MS_MOVE); 254 else if (flags & MS_BIND) 255 error = tomoyo_mount_acl(r, dev_name, dir, 256 TOMOYO_MOUNT_BIND_KEYWORD, 257 flags & ~MS_BIND); 258 else if (flags & MS_UNBINDABLE) 259 error = tomoyo_mount_acl(r, dev_name, dir, 260 TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD, 261 flags & ~MS_UNBINDABLE); 262 else if (flags & MS_PRIVATE) 263 error = tomoyo_mount_acl(r, dev_name, dir, 264 TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD, 265 flags & ~MS_PRIVATE); 266 else if (flags & MS_SLAVE) 267 error = tomoyo_mount_acl(r, dev_name, dir, 268 TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD, 269 flags & ~MS_SLAVE); 270 else if (flags & MS_SHARED) 271 error = tomoyo_mount_acl(r, dev_name, dir, 272 TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, 273 flags & ~MS_SHARED); 274 else 275 do { 276 error = tomoyo_mount_acl2(r, dev_name, dir, type, 277 flags); 278 } while (error == TOMOYO_RETRY_REQUEST); 279 if (r->mode != TOMOYO_CONFIG_ENFORCING) 280 error = 0; 281 return error; 282} 283 284/** 285 * tomoyo_mount_permission - Check permission for mount() operation. 286 * 287 * @dev_name: Name of device file. 288 * @path: Pointer to "struct path". 289 * @type: Name of filesystem type. May be NULL. 290 * @flags: Mount options. 291 * @data_page: Optional data. May be NULL. 292 * 293 * Returns 0 on success, negative value otherwise. 294 */ 295int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, 296 unsigned long flags, void *data_page) 297{ 298 struct tomoyo_request_info r; 299 int error; 300 int idx; 301 302 if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) 303 return 0; 304 if (!type) 305 type = "<NULL>"; 306 idx = tomoyo_read_lock(); 307 error = tomoyo_mount_acl(&r, dev_name, path, type, flags); 308 tomoyo_read_unlock(idx); 309 return error; 310} 311 312/** 313 * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list. 314 * 315 * @data: String to parse. 316 * @domain: Pointer to "struct tomoyo_domain_info". 317 * @is_delete: True if it is a delete request. 318 * 319 * Returns 0 on success, negative value otherwise. 320 */ 321int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, 322 const bool is_delete) 323{ 324 struct tomoyo_acl_info *ptr; 325 struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; 326 int error = is_delete ? -ENOENT : -ENOMEM; 327 char *w[4]; 328 if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) 329 return -EINVAL; 330 if (!tomoyo_parse_name_union(w[0], &e.dev_name) || 331 !tomoyo_parse_name_union(w[1], &e.dir_name) || 332 !tomoyo_parse_name_union(w[2], &e.fs_type) || 333 !tomoyo_parse_number_union(w[3], &e.flags)) 334 goto out; 335 if (mutex_lock_interruptible(&tomoyo_policy_lock)) 336 goto out; 337 list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { 338 struct tomoyo_mount_acl *acl = 339 container_of(ptr, struct tomoyo_mount_acl, head); 340 if (!tomoyo_is_same_mount_acl(acl, &e)) 341 continue; 342 acl->is_deleted = is_delete; 343 error = 0; 344 break; 345 } 346 if (!is_delete && error) { 347 struct tomoyo_mount_acl *entry = 348 tomoyo_commit_ok(&e, sizeof(e)); 349 if (entry) { 350 list_add_tail_rcu(&entry->head.list, 351 &domain->acl_info_list); 352 error = 0; 353 } 354 } 355 mutex_unlock(&tomoyo_policy_lock); 356 out: 357 tomoyo_put_name_union(&e.dev_name); 358 tomoyo_put_name_union(&e.dir_name); 359 tomoyo_put_name_union(&e.fs_type); 360 tomoyo_put_number_union(&e.flags); 361 return error; 362} 363