diff options
author | Roberto Sassu <roberto.sassu@polito.it> | 2011-03-21 11:00:53 -0400 |
---|---|---|
committer | Tyler Hicks <tyhicks@linux.vnet.ibm.com> | 2011-03-28 02:49:41 -0400 |
commit | 0e1fc5ef470cc1d157005c437a434868d59fead4 (patch) | |
tree | a15889a237ad7db00ef695d4338286e663127ee0 | |
parent | 7762e230fd31fcc1abc03ba32ee957fadc8eafb4 (diff) |
eCryptfs: verify authentication tokens before their use
Authentication tokens content may change if another requestor calls the
update() method of the corresponding key. The new function
ecryptfs_verify_auth_tok_from_key() retrieves the authentication token from
the provided key and verifies if it is still valid before being used to
encrypt or decrypt an eCryptfs file.
Signed-off-by: Roberto Sassu <roberto.sassu@polito.it>
[tyhicks: Minor formatting changes]
Signed-off-by: Tyler Hicks <tyhicks@linux.vnet.ibm.com>
-rw-r--r-- | fs/ecryptfs/ecryptfs_kernel.h | 1 | ||||
-rw-r--r-- | fs/ecryptfs/keystore.c | 211 | ||||
-rw-r--r-- | fs/ecryptfs/main.c | 4 |
3 files changed, 135 insertions, 81 deletions
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 40c93fe41cc3..bd3cafd0949d 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h | |||
@@ -331,7 +331,6 @@ struct ecryptfs_global_auth_tok { | |||
331 | u32 flags; | 331 | u32 flags; |
332 | struct list_head mount_crypt_stat_list; | 332 | struct list_head mount_crypt_stat_list; |
333 | struct key *global_auth_tok_key; | 333 | struct key *global_auth_tok_key; |
334 | struct ecryptfs_auth_tok *global_auth_tok; | ||
335 | unsigned char sig[ECRYPTFS_SIG_SIZE_HEX + 1]; | 334 | unsigned char sig[ECRYPTFS_SIG_SIZE_HEX + 1]; |
336 | }; | 335 | }; |
337 | 336 | ||
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index bd139dfad8e7..3c4039cc18d1 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c | |||
@@ -65,6 +65,24 @@ static int process_request_key_err(long err_code) | |||
65 | return rc; | 65 | return rc; |
66 | } | 66 | } |
67 | 67 | ||
68 | static int process_find_global_auth_tok_for_sig_err(int err_code) | ||
69 | { | ||
70 | int rc = err_code; | ||
71 | |||
72 | switch (err_code) { | ||
73 | case -ENOENT: | ||
74 | ecryptfs_printk(KERN_WARNING, "Missing auth tok\n"); | ||
75 | break; | ||
76 | case -EINVAL: | ||
77 | ecryptfs_printk(KERN_WARNING, "Invalid auth tok\n"); | ||
78 | break; | ||
79 | default: | ||
80 | rc = process_request_key_err(err_code); | ||
81 | break; | ||
82 | } | ||
83 | return rc; | ||
84 | } | ||
85 | |||
68 | /** | 86 | /** |
69 | * ecryptfs_parse_packet_length | 87 | * ecryptfs_parse_packet_length |
70 | * @data: Pointer to memory containing length at offset | 88 | * @data: Pointer to memory containing length at offset |
@@ -403,27 +421,117 @@ out: | |||
403 | return rc; | 421 | return rc; |
404 | } | 422 | } |
405 | 423 | ||
424 | /** | ||
425 | * ecryptfs_verify_version | ||
426 | * @version: The version number to confirm | ||
427 | * | ||
428 | * Returns zero on good version; non-zero otherwise | ||
429 | */ | ||
430 | static int ecryptfs_verify_version(u16 version) | ||
431 | { | ||
432 | int rc = 0; | ||
433 | unsigned char major; | ||
434 | unsigned char minor; | ||
435 | |||
436 | major = ((version >> 8) & 0xFF); | ||
437 | minor = (version & 0xFF); | ||
438 | if (major != ECRYPTFS_VERSION_MAJOR) { | ||
439 | ecryptfs_printk(KERN_ERR, "Major version number mismatch. " | ||
440 | "Expected [%d]; got [%d]\n", | ||
441 | ECRYPTFS_VERSION_MAJOR, major); | ||
442 | rc = -EINVAL; | ||
443 | goto out; | ||
444 | } | ||
445 | if (minor != ECRYPTFS_VERSION_MINOR) { | ||
446 | ecryptfs_printk(KERN_ERR, "Minor version number mismatch. " | ||
447 | "Expected [%d]; got [%d]\n", | ||
448 | ECRYPTFS_VERSION_MINOR, minor); | ||
449 | rc = -EINVAL; | ||
450 | goto out; | ||
451 | } | ||
452 | out: | ||
453 | return rc; | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * ecryptfs_verify_auth_tok_from_key | ||
458 | * @auth_tok_key: key containing the authentication token | ||
459 | * @auth_tok: authentication token | ||
460 | * | ||
461 | * Returns zero on valid auth tok; -EINVAL otherwise | ||
462 | */ | ||
463 | static int | ||
464 | ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key, | ||
465 | struct ecryptfs_auth_tok **auth_tok) | ||
466 | { | ||
467 | int rc = 0; | ||
468 | |||
469 | (*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key); | ||
470 | if (ecryptfs_verify_version((*auth_tok)->version)) { | ||
471 | printk(KERN_ERR "Data structure version mismatch. Userspace " | ||
472 | "tools must match eCryptfs kernel module with major " | ||
473 | "version [%d] and minor version [%d]\n", | ||
474 | ECRYPTFS_VERSION_MAJOR, ECRYPTFS_VERSION_MINOR); | ||
475 | rc = -EINVAL; | ||
476 | goto out; | ||
477 | } | ||
478 | if ((*auth_tok)->token_type != ECRYPTFS_PASSWORD | ||
479 | && (*auth_tok)->token_type != ECRYPTFS_PRIVATE_KEY) { | ||
480 | printk(KERN_ERR "Invalid auth_tok structure " | ||
481 | "returned from key query\n"); | ||
482 | rc = -EINVAL; | ||
483 | goto out; | ||
484 | } | ||
485 | out: | ||
486 | return rc; | ||
487 | } | ||
488 | |||
406 | static int | 489 | static int |
407 | ecryptfs_find_global_auth_tok_for_sig( | 490 | ecryptfs_find_global_auth_tok_for_sig( |
408 | struct ecryptfs_global_auth_tok **global_auth_tok, | 491 | struct key **auth_tok_key, |
492 | struct ecryptfs_auth_tok **auth_tok, | ||
409 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig) | 493 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat, char *sig) |
410 | { | 494 | { |
411 | struct ecryptfs_global_auth_tok *walker; | 495 | struct ecryptfs_global_auth_tok *walker; |
412 | int rc = 0; | 496 | int rc = 0; |
413 | 497 | ||
414 | (*global_auth_tok) = NULL; | 498 | (*auth_tok_key) = NULL; |
499 | (*auth_tok) = NULL; | ||
415 | mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex); | 500 | mutex_lock(&mount_crypt_stat->global_auth_tok_list_mutex); |
416 | list_for_each_entry(walker, | 501 | list_for_each_entry(walker, |
417 | &mount_crypt_stat->global_auth_tok_list, | 502 | &mount_crypt_stat->global_auth_tok_list, |
418 | mount_crypt_stat_list) { | 503 | mount_crypt_stat_list) { |
419 | if (memcmp(walker->sig, sig, ECRYPTFS_SIG_SIZE_HEX) == 0) { | 504 | if (memcmp(walker->sig, sig, ECRYPTFS_SIG_SIZE_HEX)) |
420 | rc = key_validate(walker->global_auth_tok_key); | 505 | continue; |
421 | if (!rc) | 506 | |
422 | (*global_auth_tok) = walker; | 507 | if (walker->flags & ECRYPTFS_AUTH_TOK_INVALID) { |
508 | rc = -EINVAL; | ||
423 | goto out; | 509 | goto out; |
424 | } | 510 | } |
511 | |||
512 | rc = key_validate(walker->global_auth_tok_key); | ||
513 | if (rc) { | ||
514 | if (rc == -EKEYEXPIRED) | ||
515 | goto out; | ||
516 | goto out_invalid_auth_tok; | ||
517 | } | ||
518 | |||
519 | rc = ecryptfs_verify_auth_tok_from_key( | ||
520 | walker->global_auth_tok_key, auth_tok); | ||
521 | if (rc) | ||
522 | goto out_invalid_auth_tok; | ||
523 | |||
524 | (*auth_tok_key) = walker->global_auth_tok_key; | ||
525 | key_get(*auth_tok_key); | ||
526 | goto out; | ||
425 | } | 527 | } |
426 | rc = -EINVAL; | 528 | rc = -ENOENT; |
529 | goto out; | ||
530 | out_invalid_auth_tok: | ||
531 | printk(KERN_WARNING "Invalidating auth tok with sig = [%s]\n", sig); | ||
532 | walker->flags |= ECRYPTFS_AUTH_TOK_INVALID; | ||
533 | key_put(walker->global_auth_tok_key); | ||
534 | walker->global_auth_tok_key = NULL; | ||
427 | out: | 535 | out: |
428 | mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex); | 536 | mutex_unlock(&mount_crypt_stat->global_auth_tok_list_mutex); |
429 | return rc; | 537 | return rc; |
@@ -451,14 +559,11 @@ ecryptfs_find_auth_tok_for_sig( | |||
451 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat, | 559 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat, |
452 | char *sig) | 560 | char *sig) |
453 | { | 561 | { |
454 | struct ecryptfs_global_auth_tok *global_auth_tok; | ||
455 | int rc = 0; | 562 | int rc = 0; |
456 | 563 | ||
457 | (*auth_tok_key) = NULL; | 564 | rc = ecryptfs_find_global_auth_tok_for_sig(auth_tok_key, auth_tok, |
458 | (*auth_tok) = NULL; | 565 | mount_crypt_stat, sig); |
459 | if (ecryptfs_find_global_auth_tok_for_sig(&global_auth_tok, | 566 | if (rc == -ENOENT) { |
460 | mount_crypt_stat, sig)) { | ||
461 | |||
462 | /* if the flag ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY is set in the | 567 | /* if the flag ECRYPTFS_GLOBAL_MOUNT_AUTH_TOK_ONLY is set in the |
463 | * mount_crypt_stat structure, we prevent to use auth toks that | 568 | * mount_crypt_stat structure, we prevent to use auth toks that |
464 | * are not inserted through the ecryptfs_add_global_auth_tok | 569 | * are not inserted through the ecryptfs_add_global_auth_tok |
@@ -470,8 +575,7 @@ ecryptfs_find_auth_tok_for_sig( | |||
470 | 575 | ||
471 | rc = ecryptfs_keyring_auth_tok_for_sig(auth_tok_key, auth_tok, | 576 | rc = ecryptfs_keyring_auth_tok_for_sig(auth_tok_key, auth_tok, |
472 | sig); | 577 | sig); |
473 | } else | 578 | } |
474 | (*auth_tok) = global_auth_tok->global_auth_tok; | ||
475 | return rc; | 579 | return rc; |
476 | } | 580 | } |
477 | 581 | ||
@@ -1520,38 +1624,6 @@ out: | |||
1520 | return rc; | 1624 | return rc; |
1521 | } | 1625 | } |
1522 | 1626 | ||
1523 | /** | ||
1524 | * ecryptfs_verify_version | ||
1525 | * @version: The version number to confirm | ||
1526 | * | ||
1527 | * Returns zero on good version; non-zero otherwise | ||
1528 | */ | ||
1529 | static int ecryptfs_verify_version(u16 version) | ||
1530 | { | ||
1531 | int rc = 0; | ||
1532 | unsigned char major; | ||
1533 | unsigned char minor; | ||
1534 | |||
1535 | major = ((version >> 8) & 0xFF); | ||
1536 | minor = (version & 0xFF); | ||
1537 | if (major != ECRYPTFS_VERSION_MAJOR) { | ||
1538 | ecryptfs_printk(KERN_ERR, "Major version number mismatch. " | ||
1539 | "Expected [%d]; got [%d]\n", | ||
1540 | ECRYPTFS_VERSION_MAJOR, major); | ||
1541 | rc = -EINVAL; | ||
1542 | goto out; | ||
1543 | } | ||
1544 | if (minor != ECRYPTFS_VERSION_MINOR) { | ||
1545 | ecryptfs_printk(KERN_ERR, "Minor version number mismatch. " | ||
1546 | "Expected [%d]; got [%d]\n", | ||
1547 | ECRYPTFS_VERSION_MINOR, minor); | ||
1548 | rc = -EINVAL; | ||
1549 | goto out; | ||
1550 | } | ||
1551 | out: | ||
1552 | return rc; | ||
1553 | } | ||
1554 | |||
1555 | int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key, | 1627 | int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key, |
1556 | struct ecryptfs_auth_tok **auth_tok, | 1628 | struct ecryptfs_auth_tok **auth_tok, |
1557 | char *sig) | 1629 | char *sig) |
@@ -1566,29 +1638,12 @@ int ecryptfs_keyring_auth_tok_for_sig(struct key **auth_tok_key, | |||
1566 | (*auth_tok_key) = NULL; | 1638 | (*auth_tok_key) = NULL; |
1567 | goto out; | 1639 | goto out; |
1568 | } | 1640 | } |
1569 | (*auth_tok) = ecryptfs_get_key_payload_data(*auth_tok_key); | 1641 | |
1570 | if (ecryptfs_verify_version((*auth_tok)->version)) { | 1642 | rc = ecryptfs_verify_auth_tok_from_key(*auth_tok_key, auth_tok); |
1571 | printk(KERN_ERR | ||
1572 | "Data structure version mismatch. " | ||
1573 | "Userspace tools must match eCryptfs " | ||
1574 | "kernel module with major version [%d] " | ||
1575 | "and minor version [%d]\n", | ||
1576 | ECRYPTFS_VERSION_MAJOR, | ||
1577 | ECRYPTFS_VERSION_MINOR); | ||
1578 | rc = -EINVAL; | ||
1579 | goto out_release_key; | ||
1580 | } | ||
1581 | if ((*auth_tok)->token_type != ECRYPTFS_PASSWORD | ||
1582 | && (*auth_tok)->token_type != ECRYPTFS_PRIVATE_KEY) { | ||
1583 | printk(KERN_ERR "Invalid auth_tok structure " | ||
1584 | "returned from key query\n"); | ||
1585 | rc = -EINVAL; | ||
1586 | goto out_release_key; | ||
1587 | } | ||
1588 | out_release_key: | ||
1589 | if (rc) { | 1643 | if (rc) { |
1590 | key_put(*auth_tok_key); | 1644 | key_put(*auth_tok_key); |
1591 | (*auth_tok_key) = NULL; | 1645 | (*auth_tok_key) = NULL; |
1646 | goto out; | ||
1592 | } | 1647 | } |
1593 | out: | 1648 | out: |
1594 | return rc; | 1649 | return rc; |
@@ -2325,7 +2380,7 @@ ecryptfs_generate_key_packet_set(char *dest_base, | |||
2325 | size_t max) | 2380 | size_t max) |
2326 | { | 2381 | { |
2327 | struct ecryptfs_auth_tok *auth_tok; | 2382 | struct ecryptfs_auth_tok *auth_tok; |
2328 | struct ecryptfs_global_auth_tok *global_auth_tok; | 2383 | struct key *auth_tok_key = NULL; |
2329 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat = | 2384 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat = |
2330 | &ecryptfs_superblock_to_private( | 2385 | &ecryptfs_superblock_to_private( |
2331 | ecryptfs_dentry->d_sb)->mount_crypt_stat; | 2386 | ecryptfs_dentry->d_sb)->mount_crypt_stat; |
@@ -2344,21 +2399,16 @@ ecryptfs_generate_key_packet_set(char *dest_base, | |||
2344 | list_for_each_entry(key_sig, &crypt_stat->keysig_list, | 2399 | list_for_each_entry(key_sig, &crypt_stat->keysig_list, |
2345 | crypt_stat_list) { | 2400 | crypt_stat_list) { |
2346 | memset(key_rec, 0, sizeof(*key_rec)); | 2401 | memset(key_rec, 0, sizeof(*key_rec)); |
2347 | rc = ecryptfs_find_global_auth_tok_for_sig(&global_auth_tok, | 2402 | rc = ecryptfs_find_global_auth_tok_for_sig(&auth_tok_key, |
2403 | &auth_tok, | ||
2348 | mount_crypt_stat, | 2404 | mount_crypt_stat, |
2349 | key_sig->keysig); | 2405 | key_sig->keysig); |
2350 | if (rc) { | 2406 | if (rc) { |
2351 | printk(KERN_ERR "Error attempting to get the global " | 2407 | printk(KERN_WARNING "Unable to retrieve auth tok with " |
2352 | "auth_tok; rc = [%d]\n", rc); | 2408 | "sig = [%s]\n", key_sig->keysig); |
2409 | rc = process_find_global_auth_tok_for_sig_err(rc); | ||
2353 | goto out_free; | 2410 | goto out_free; |
2354 | } | 2411 | } |
2355 | if (global_auth_tok->flags & ECRYPTFS_AUTH_TOK_INVALID) { | ||
2356 | printk(KERN_WARNING | ||
2357 | "Skipping invalid auth tok with sig = [%s]\n", | ||
2358 | global_auth_tok->sig); | ||
2359 | continue; | ||
2360 | } | ||
2361 | auth_tok = global_auth_tok->global_auth_tok; | ||
2362 | if (auth_tok->token_type == ECRYPTFS_PASSWORD) { | 2412 | if (auth_tok->token_type == ECRYPTFS_PASSWORD) { |
2363 | rc = write_tag_3_packet((dest_base + (*len)), | 2413 | rc = write_tag_3_packet((dest_base + (*len)), |
2364 | &max, auth_tok, | 2414 | &max, auth_tok, |
@@ -2396,6 +2446,8 @@ ecryptfs_generate_key_packet_set(char *dest_base, | |||
2396 | rc = -EINVAL; | 2446 | rc = -EINVAL; |
2397 | goto out_free; | 2447 | goto out_free; |
2398 | } | 2448 | } |
2449 | key_put(auth_tok_key); | ||
2450 | auth_tok_key = NULL; | ||
2399 | } | 2451 | } |
2400 | if (likely(max > 0)) { | 2452 | if (likely(max > 0)) { |
2401 | dest_base[(*len)] = 0x00; | 2453 | dest_base[(*len)] = 0x00; |
@@ -2408,6 +2460,9 @@ out_free: | |||
2408 | out: | 2460 | out: |
2409 | if (rc) | 2461 | if (rc) |
2410 | (*len) = 0; | 2462 | (*len) = 0; |
2463 | if (auth_tok_key) | ||
2464 | key_put(auth_tok_key); | ||
2465 | |||
2411 | mutex_unlock(&crypt_stat->keysig_list_mutex); | 2466 | mutex_unlock(&crypt_stat->keysig_list_mutex); |
2412 | return rc; | 2467 | return rc; |
2413 | } | 2468 | } |
diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 63e412cf0fa1..520d05f5ad01 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c | |||
@@ -239,14 +239,14 @@ static int ecryptfs_init_global_auth_toks( | |||
239 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat) | 239 | struct ecryptfs_mount_crypt_stat *mount_crypt_stat) |
240 | { | 240 | { |
241 | struct ecryptfs_global_auth_tok *global_auth_tok; | 241 | struct ecryptfs_global_auth_tok *global_auth_tok; |
242 | struct ecryptfs_auth_tok *auth_tok; | ||
242 | int rc = 0; | 243 | int rc = 0; |
243 | 244 | ||
244 | list_for_each_entry(global_auth_tok, | 245 | list_for_each_entry(global_auth_tok, |
245 | &mount_crypt_stat->global_auth_tok_list, | 246 | &mount_crypt_stat->global_auth_tok_list, |
246 | mount_crypt_stat_list) { | 247 | mount_crypt_stat_list) { |
247 | rc = ecryptfs_keyring_auth_tok_for_sig( | 248 | rc = ecryptfs_keyring_auth_tok_for_sig( |
248 | &global_auth_tok->global_auth_tok_key, | 249 | &global_auth_tok->global_auth_tok_key, &auth_tok, |
249 | &global_auth_tok->global_auth_tok, | ||
250 | global_auth_tok->sig); | 250 | global_auth_tok->sig); |
251 | if (rc) { | 251 | if (rc) { |
252 | printk(KERN_ERR "Could not find valid key in user " | 252 | printk(KERN_ERR "Could not find valid key in user " |