diff options
author | Michael Halcrow <mhalcrow@us.ibm.com> | 2007-02-12 03:53:46 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-12 12:48:36 -0500 |
commit | dd2a3b7ad98f8482cae481cad89dfed5eee48365 (patch) | |
tree | 986c09754176ea4c6e8308c6e2cdbf3fc0658a0b /fs/ecryptfs/crypto.c | |
parent | 17398957aa0a05ef62535060b41d103590dcc533 (diff) |
[PATCH] eCryptfs: Generalize metadata read/write
Generalize the metadata reading and writing mechanisms, with two targets for
now: metadata in file header and metadata in the user.ecryptfs xattr of the
lower file.
[akpm@osdl.org: printk warning fix]
[bunk@stusta.de: make some needlessly global code static]
Signed-off-by: Michael Halcrow <mhalcrow@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ecryptfs/crypto.c')
-rw-r--r-- | fs/ecryptfs/crypto.c | 234 |
1 files changed, 178 insertions, 56 deletions
diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 6d85aabb0179..96fa40a48b4f 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * Copyright (C) 1997-2004 Erez Zadok | 4 | * Copyright (C) 1997-2004 Erez Zadok |
5 | * Copyright (C) 2001-2004 Stony Brook University | 5 | * Copyright (C) 2001-2004 Stony Brook University |
6 | * Copyright (C) 2004-2006 International Business Machines Corp. | 6 | * Copyright (C) 2004-2007 International Business Machines Corp. |
7 | * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> | 7 | * Author(s): Michael A. Halcrow <mahalcro@us.ibm.com> |
8 | * Michael C. Thompson <mcthomps@us.ibm.com> | 8 | * Michael C. Thompson <mcthomps@us.ibm.com> |
9 | * | 9 | * |
@@ -863,7 +863,10 @@ void ecryptfs_set_default_sizes(struct ecryptfs_crypt_stat *crypt_stat) | |||
863 | ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE; | 863 | ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE; |
864 | } else | 864 | } else |
865 | crypt_stat->header_extent_size = PAGE_CACHE_SIZE; | 865 | crypt_stat->header_extent_size = PAGE_CACHE_SIZE; |
866 | crypt_stat->num_header_extents_at_front = 1; | 866 | if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) |
867 | crypt_stat->num_header_extents_at_front = 0; | ||
868 | else | ||
869 | crypt_stat->num_header_extents_at_front = 1; | ||
867 | } | 870 | } |
868 | 871 | ||
869 | /** | 872 | /** |
@@ -1021,7 +1024,7 @@ int ecryptfs_new_file_context(struct dentry *ecryptfs_dentry) | |||
1021 | * | 1024 | * |
1022 | * Returns one if marker found; zero if not found | 1025 | * Returns one if marker found; zero if not found |
1023 | */ | 1026 | */ |
1024 | int contains_ecryptfs_marker(char *data) | 1027 | static int contains_ecryptfs_marker(char *data) |
1025 | { | 1028 | { |
1026 | u32 m_1, m_2; | 1029 | u32 m_1, m_2; |
1027 | 1030 | ||
@@ -1047,7 +1050,8 @@ struct ecryptfs_flag_map_elem { | |||
1047 | /* Add support for additional flags by adding elements here. */ | 1050 | /* Add support for additional flags by adding elements here. */ |
1048 | static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = { | 1051 | static struct ecryptfs_flag_map_elem ecryptfs_flag_map[] = { |
1049 | {0x00000001, ECRYPTFS_ENABLE_HMAC}, | 1052 | {0x00000001, ECRYPTFS_ENABLE_HMAC}, |
1050 | {0x00000002, ECRYPTFS_ENCRYPTED} | 1053 | {0x00000002, ECRYPTFS_ENCRYPTED}, |
1054 | {0x00000004, ECRYPTFS_METADATA_IN_XATTR} | ||
1051 | }; | 1055 | }; |
1052 | 1056 | ||
1053 | /** | 1057 | /** |
@@ -1207,8 +1211,8 @@ int ecryptfs_cipher_code_to_string(char *str, u16 cipher_code) | |||
1207 | * | 1211 | * |
1208 | * Returns zero on success; non-zero otherwise | 1212 | * Returns zero on success; non-zero otherwise |
1209 | */ | 1213 | */ |
1210 | int ecryptfs_read_header_region(char *data, struct dentry *dentry, | 1214 | static int ecryptfs_read_header_region(char *data, struct dentry *dentry, |
1211 | struct vfsmount *mnt) | 1215 | struct vfsmount *mnt) |
1212 | { | 1216 | { |
1213 | struct file *lower_file; | 1217 | struct file *lower_file; |
1214 | mm_segment_t oldfs; | 1218 | mm_segment_t oldfs; |
@@ -1237,6 +1241,21 @@ out: | |||
1237 | return rc; | 1241 | return rc; |
1238 | } | 1242 | } |
1239 | 1243 | ||
1244 | int ecryptfs_read_and_validate_header_region(char *data, struct dentry *dentry, | ||
1245 | struct vfsmount *mnt) | ||
1246 | { | ||
1247 | int rc; | ||
1248 | |||
1249 | rc = ecryptfs_read_header_region(data, dentry, mnt); | ||
1250 | if (rc) | ||
1251 | goto out; | ||
1252 | if (!contains_ecryptfs_marker(data + ECRYPTFS_FILE_SIZE_BYTES)) | ||
1253 | rc = -EINVAL; | ||
1254 | out: | ||
1255 | return rc; | ||
1256 | } | ||
1257 | |||
1258 | |||
1240 | static void | 1259 | static void |
1241 | write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat, | 1260 | write_header_metadata(char *virt, struct ecryptfs_crypt_stat *crypt_stat, |
1242 | size_t *written) | 1261 | size_t *written) |
@@ -1288,9 +1307,9 @@ struct kmem_cache *ecryptfs_header_cache_2; | |||
1288 | * | 1307 | * |
1289 | * Returns zero on success | 1308 | * Returns zero on success |
1290 | */ | 1309 | */ |
1291 | int ecryptfs_write_headers_virt(char *page_virt, | 1310 | static int ecryptfs_write_headers_virt(char *page_virt, size_t *size, |
1292 | struct ecryptfs_crypt_stat *crypt_stat, | 1311 | struct ecryptfs_crypt_stat *crypt_stat, |
1293 | struct dentry *ecryptfs_dentry) | 1312 | struct dentry *ecryptfs_dentry) |
1294 | { | 1313 | { |
1295 | int rc; | 1314 | int rc; |
1296 | size_t written; | 1315 | size_t written; |
@@ -1309,11 +1328,53 @@ int ecryptfs_write_headers_virt(char *page_virt, | |||
1309 | if (rc) | 1328 | if (rc) |
1310 | ecryptfs_printk(KERN_WARNING, "Error generating key packet " | 1329 | ecryptfs_printk(KERN_WARNING, "Error generating key packet " |
1311 | "set; rc = [%d]\n", rc); | 1330 | "set; rc = [%d]\n", rc); |
1331 | if (size) { | ||
1332 | offset += written; | ||
1333 | *size = offset; | ||
1334 | } | ||
1335 | return rc; | ||
1336 | } | ||
1337 | |||
1338 | static int ecryptfs_write_metadata_to_contents(struct ecryptfs_crypt_stat *crypt_stat, | ||
1339 | struct file *lower_file, | ||
1340 | char *page_virt) | ||
1341 | { | ||
1342 | mm_segment_t oldfs; | ||
1343 | int current_header_page; | ||
1344 | int header_pages; | ||
1345 | |||
1346 | lower_file->f_pos = 0; | ||
1347 | oldfs = get_fs(); | ||
1348 | set_fs(get_ds()); | ||
1349 | lower_file->f_op->write(lower_file, (char __user *)page_virt, | ||
1350 | PAGE_CACHE_SIZE, &lower_file->f_pos); | ||
1351 | header_pages = ((crypt_stat->header_extent_size | ||
1352 | * crypt_stat->num_header_extents_at_front) | ||
1353 | / PAGE_CACHE_SIZE); | ||
1354 | memset(page_virt, 0, PAGE_CACHE_SIZE); | ||
1355 | current_header_page = 1; | ||
1356 | while (current_header_page < header_pages) { | ||
1357 | lower_file->f_op->write(lower_file, (char __user *)page_virt, | ||
1358 | PAGE_CACHE_SIZE, &lower_file->f_pos); | ||
1359 | current_header_page++; | ||
1360 | } | ||
1361 | set_fs(oldfs); | ||
1362 | return 0; | ||
1363 | } | ||
1364 | |||
1365 | static int ecryptfs_write_metadata_to_xattr(struct dentry *ecryptfs_dentry, | ||
1366 | struct ecryptfs_crypt_stat *crypt_stat, | ||
1367 | char *page_virt, size_t size) | ||
1368 | { | ||
1369 | int rc; | ||
1370 | |||
1371 | rc = ecryptfs_setxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME, page_virt, | ||
1372 | size, 0); | ||
1312 | return rc; | 1373 | return rc; |
1313 | } | 1374 | } |
1314 | 1375 | ||
1315 | /** | 1376 | /** |
1316 | * ecryptfs_write_headers | 1377 | * ecryptfs_write_metadata |
1317 | * @lower_file: The lower file struct, which was returned from dentry_open | 1378 | * @lower_file: The lower file struct, which was returned from dentry_open |
1318 | * | 1379 | * |
1319 | * Write the file headers out. This will likely involve a userspace | 1380 | * Write the file headers out. This will likely involve a userspace |
@@ -1324,14 +1385,12 @@ int ecryptfs_write_headers_virt(char *page_virt, | |||
1324 | * | 1385 | * |
1325 | * Returns zero on success; non-zero on error | 1386 | * Returns zero on success; non-zero on error |
1326 | */ | 1387 | */ |
1327 | int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, | 1388 | int ecryptfs_write_metadata(struct dentry *ecryptfs_dentry, |
1328 | struct file *lower_file) | 1389 | struct file *lower_file) |
1329 | { | 1390 | { |
1330 | mm_segment_t oldfs; | ||
1331 | struct ecryptfs_crypt_stat *crypt_stat; | 1391 | struct ecryptfs_crypt_stat *crypt_stat; |
1332 | char *page_virt; | 1392 | char *page_virt; |
1333 | int current_header_page; | 1393 | size_t size; |
1334 | int header_pages; | ||
1335 | int rc = 0; | 1394 | int rc = 0; |
1336 | 1395 | ||
1337 | crypt_stat = &ecryptfs_inode_to_private( | 1396 | crypt_stat = &ecryptfs_inode_to_private( |
@@ -1358,48 +1417,36 @@ int ecryptfs_write_headers(struct dentry *ecryptfs_dentry, | |||
1358 | rc = -ENOMEM; | 1417 | rc = -ENOMEM; |
1359 | goto out; | 1418 | goto out; |
1360 | } | 1419 | } |
1361 | 1420 | rc = ecryptfs_write_headers_virt(page_virt, &size, crypt_stat, | |
1362 | rc = ecryptfs_write_headers_virt(page_virt, crypt_stat, | 1421 | ecryptfs_dentry); |
1363 | ecryptfs_dentry); | ||
1364 | if (unlikely(rc)) { | 1422 | if (unlikely(rc)) { |
1365 | ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n"); | 1423 | ecryptfs_printk(KERN_ERR, "Error whilst writing headers\n"); |
1366 | memset(page_virt, 0, PAGE_CACHE_SIZE); | 1424 | memset(page_virt, 0, PAGE_CACHE_SIZE); |
1367 | goto out_free; | 1425 | goto out_free; |
1368 | } | 1426 | } |
1369 | ecryptfs_printk(KERN_DEBUG, | 1427 | if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) |
1370 | "Writing key packet set to underlying file\n"); | 1428 | rc = ecryptfs_write_metadata_to_xattr(ecryptfs_dentry, |
1371 | lower_file->f_pos = 0; | 1429 | crypt_stat, page_virt, |
1372 | oldfs = get_fs(); | 1430 | size); |
1373 | set_fs(get_ds()); | 1431 | else |
1374 | ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->" | 1432 | rc = ecryptfs_write_metadata_to_contents(crypt_stat, lower_file, |
1375 | "write() w/ header page; lower_file->f_pos = " | 1433 | page_virt); |
1376 | "[0x%.16x]\n", lower_file->f_pos); | 1434 | if (rc) { |
1377 | lower_file->f_op->write(lower_file, (char __user *)page_virt, | 1435 | printk(KERN_ERR "Error writing metadata out to lower file; " |
1378 | PAGE_CACHE_SIZE, &lower_file->f_pos); | 1436 | "rc = [%d]\n", rc); |
1379 | header_pages = ((crypt_stat->header_extent_size | 1437 | goto out_free; |
1380 | * crypt_stat->num_header_extents_at_front) | ||
1381 | / PAGE_CACHE_SIZE); | ||
1382 | memset(page_virt, 0, PAGE_CACHE_SIZE); | ||
1383 | current_header_page = 1; | ||
1384 | while (current_header_page < header_pages) { | ||
1385 | ecryptfs_printk(KERN_DEBUG, "Calling lower_file->f_op->" | ||
1386 | "write() w/ zero'd page; lower_file->f_pos = " | ||
1387 | "[0x%.16x]\n", lower_file->f_pos); | ||
1388 | lower_file->f_op->write(lower_file, (char __user *)page_virt, | ||
1389 | PAGE_CACHE_SIZE, &lower_file->f_pos); | ||
1390 | current_header_page++; | ||
1391 | } | 1438 | } |
1392 | set_fs(oldfs); | ||
1393 | ecryptfs_printk(KERN_DEBUG, | ||
1394 | "Done writing key packet set to underlying file.\n"); | ||
1395 | out_free: | 1439 | out_free: |
1396 | kmem_cache_free(ecryptfs_header_cache_0, page_virt); | 1440 | kmem_cache_free(ecryptfs_header_cache_0, page_virt); |
1397 | out: | 1441 | out: |
1398 | return rc; | 1442 | return rc; |
1399 | } | 1443 | } |
1400 | 1444 | ||
1445 | #define ECRYPTFS_DONT_VALIDATE_HEADER_SIZE 0 | ||
1446 | #define ECRYPTFS_VALIDATE_HEADER_SIZE 1 | ||
1401 | static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat, | 1447 | static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat, |
1402 | char *virt, int *bytes_read) | 1448 | char *virt, int *bytes_read, |
1449 | int validate_header_size) | ||
1403 | { | 1450 | { |
1404 | int rc = 0; | 1451 | int rc = 0; |
1405 | u32 header_extent_size; | 1452 | u32 header_extent_size; |
@@ -1414,9 +1461,10 @@ static int parse_header_metadata(struct ecryptfs_crypt_stat *crypt_stat, | |||
1414 | crypt_stat->num_header_extents_at_front = | 1461 | crypt_stat->num_header_extents_at_front = |
1415 | (int)num_header_extents_at_front; | 1462 | (int)num_header_extents_at_front; |
1416 | (*bytes_read) = 6; | 1463 | (*bytes_read) = 6; |
1417 | if ((crypt_stat->header_extent_size | 1464 | if ((validate_header_size == ECRYPTFS_VALIDATE_HEADER_SIZE) |
1418 | * crypt_stat->num_header_extents_at_front) | 1465 | && ((crypt_stat->header_extent_size |
1419 | < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE) { | 1466 | * crypt_stat->num_header_extents_at_front) |
1467 | < ECRYPTFS_MINIMUM_HEADER_EXTENT_SIZE)) { | ||
1420 | rc = -EINVAL; | 1468 | rc = -EINVAL; |
1421 | ecryptfs_printk(KERN_WARNING, "Invalid header extent size: " | 1469 | ecryptfs_printk(KERN_WARNING, "Invalid header extent size: " |
1422 | "[%d]\n", crypt_stat->header_extent_size); | 1470 | "[%d]\n", crypt_stat->header_extent_size); |
@@ -1447,7 +1495,8 @@ static void set_default_header_data(struct ecryptfs_crypt_stat *crypt_stat) | |||
1447 | */ | 1495 | */ |
1448 | static int ecryptfs_read_headers_virt(char *page_virt, | 1496 | static int ecryptfs_read_headers_virt(char *page_virt, |
1449 | struct ecryptfs_crypt_stat *crypt_stat, | 1497 | struct ecryptfs_crypt_stat *crypt_stat, |
1450 | struct dentry *ecryptfs_dentry) | 1498 | struct dentry *ecryptfs_dentry, |
1499 | int validate_header_size) | ||
1451 | { | 1500 | { |
1452 | int rc = 0; | 1501 | int rc = 0; |
1453 | int offset; | 1502 | int offset; |
@@ -1481,7 +1530,7 @@ static int ecryptfs_read_headers_virt(char *page_virt, | |||
1481 | offset += bytes_read; | 1530 | offset += bytes_read; |
1482 | if (crypt_stat->file_version >= 1) { | 1531 | if (crypt_stat->file_version >= 1) { |
1483 | rc = parse_header_metadata(crypt_stat, (page_virt + offset), | 1532 | rc = parse_header_metadata(crypt_stat, (page_virt + offset), |
1484 | &bytes_read); | 1533 | &bytes_read, validate_header_size); |
1485 | if (rc) { | 1534 | if (rc) { |
1486 | ecryptfs_printk(KERN_WARNING, "Error reading header " | 1535 | ecryptfs_printk(KERN_WARNING, "Error reading header " |
1487 | "metadata; rc = [%d]\n", rc); | 1536 | "metadata; rc = [%d]\n", rc); |
@@ -1496,12 +1545,60 @@ out: | |||
1496 | } | 1545 | } |
1497 | 1546 | ||
1498 | /** | 1547 | /** |
1499 | * ecryptfs_read_headers | 1548 | * ecryptfs_read_xattr_region |
1549 | * | ||
1550 | * Attempts to read the crypto metadata from the extended attribute | ||
1551 | * region of the lower file. | ||
1552 | */ | ||
1553 | int ecryptfs_read_xattr_region(char *page_virt, struct dentry *ecryptfs_dentry) | ||
1554 | { | ||
1555 | ssize_t size; | ||
1556 | int rc = 0; | ||
1557 | |||
1558 | size = ecryptfs_getxattr(ecryptfs_dentry, ECRYPTFS_XATTR_NAME, | ||
1559 | page_virt, ECRYPTFS_DEFAULT_EXTENT_SIZE); | ||
1560 | if (size < 0) { | ||
1561 | printk(KERN_DEBUG "Error attempting to read the [%s] " | ||
1562 | "xattr from the lower file; return value = [%zd]\n", | ||
1563 | ECRYPTFS_XATTR_NAME, size); | ||
1564 | rc = -EINVAL; | ||
1565 | goto out; | ||
1566 | } | ||
1567 | out: | ||
1568 | return rc; | ||
1569 | } | ||
1570 | |||
1571 | int ecryptfs_read_and_validate_xattr_region(char *page_virt, | ||
1572 | struct dentry *ecryptfs_dentry) | ||
1573 | { | ||
1574 | int rc; | ||
1575 | |||
1576 | rc = ecryptfs_read_xattr_region(page_virt, ecryptfs_dentry); | ||
1577 | if (rc) | ||
1578 | goto out; | ||
1579 | if (!contains_ecryptfs_marker(page_virt + ECRYPTFS_FILE_SIZE_BYTES)) { | ||
1580 | printk(KERN_WARNING "Valid data found in [%s] xattr, but " | ||
1581 | "the marker is invalid\n", ECRYPTFS_XATTR_NAME); | ||
1582 | rc = -EINVAL; | ||
1583 | } | ||
1584 | out: | ||
1585 | return rc; | ||
1586 | } | ||
1587 | |||
1588 | /** | ||
1589 | * ecryptfs_read_metadata | ||
1590 | * | ||
1591 | * Common entry point for reading file metadata. From here, we could | ||
1592 | * retrieve the header information from the header region of the file, | ||
1593 | * the xattr region of the file, or some other repostory that is | ||
1594 | * stored separately from the file itself. The current implementation | ||
1595 | * supports retrieving the metadata information from the file contents | ||
1596 | * and from the xattr region. | ||
1500 | * | 1597 | * |
1501 | * Returns zero if valid headers found and parsed; non-zero otherwise | 1598 | * Returns zero if valid headers found and parsed; non-zero otherwise |
1502 | */ | 1599 | */ |
1503 | int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, | 1600 | int ecryptfs_read_metadata(struct dentry *ecryptfs_dentry, |
1504 | struct file *lower_file) | 1601 | struct file *lower_file) |
1505 | { | 1602 | { |
1506 | int rc = 0; | 1603 | int rc = 0; |
1507 | char *page_virt = NULL; | 1604 | char *page_virt = NULL; |
@@ -1530,11 +1627,36 @@ int ecryptfs_read_headers(struct dentry *ecryptfs_dentry, | |||
1530 | goto out; | 1627 | goto out; |
1531 | } | 1628 | } |
1532 | rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, | 1629 | rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, |
1533 | ecryptfs_dentry); | 1630 | ecryptfs_dentry, |
1631 | ECRYPTFS_VALIDATE_HEADER_SIZE); | ||
1534 | if (rc) { | 1632 | if (rc) { |
1535 | ecryptfs_printk(KERN_DEBUG, "Valid eCryptfs headers not " | 1633 | rc = ecryptfs_read_xattr_region(page_virt, |
1536 | "found\n"); | 1634 | ecryptfs_dentry); |
1537 | rc = -EINVAL; | 1635 | if (rc) { |
1636 | printk(KERN_DEBUG "Valid eCryptfs headers not found in " | ||
1637 | "file header region or xattr region\n"); | ||
1638 | rc = -EINVAL; | ||
1639 | goto out; | ||
1640 | } | ||
1641 | rc = ecryptfs_read_headers_virt(page_virt, crypt_stat, | ||
1642 | ecryptfs_dentry, | ||
1643 | ECRYPTFS_DONT_VALIDATE_HEADER_SIZE); | ||
1644 | if (rc) { | ||
1645 | printk(KERN_DEBUG "Valid eCryptfs headers not found in " | ||
1646 | "file xattr region either\n"); | ||
1647 | rc = -EINVAL; | ||
1648 | } | ||
1649 | if (crypt_stat->mount_crypt_stat->flags | ||
1650 | & ECRYPTFS_XATTR_METADATA_ENABLED) { | ||
1651 | crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; | ||
1652 | } else { | ||
1653 | printk(KERN_WARNING "Attempt to access file with " | ||
1654 | "crypto metadata only in the extended attribute " | ||
1655 | "region, but eCryptfs was mounted without " | ||
1656 | "xattr support enabled. eCryptfs will not treat " | ||
1657 | "this like an encrypted file.\n"); | ||
1658 | rc = -EINVAL; | ||
1659 | } | ||
1538 | } | 1660 | } |
1539 | out: | 1661 | out: |
1540 | if (page_virt) { | 1662 | if (page_virt) { |