|
1 | 1 | from datetime import datetime, timezone |
2 | 2 | from io import BytesIO |
3 | 3 | from pathlib import Path |
| 4 | +from textwrap import dedent |
| 5 | + |
| 6 | +import pytest |
4 | 7 |
|
5 | 8 | from dissect.target.filesystem import VirtualFilesystem |
6 | 9 | from dissect.target.plugins.os.unix.shadow import ShadowPlugin |
@@ -50,3 +53,66 @@ def test_unix_shadow_backup_file(target_unix_users: Target, fs_unix: VirtualFile |
50 | 53 | assert results[0].name == "test" |
51 | 54 | assert results[1].name == "other-user" |
52 | 55 | assert results[0].hash == results[1].hash |
| 56 | + |
| 57 | + |
| 58 | +def test_unix_shadow_invalid_shent( |
| 59 | + caplog: pytest.LogCaptureFixture, target_unix_users: Target, fs_unix: VirtualFilesystem |
| 60 | +) -> None: |
| 61 | + """test if we can parse invalid day values in shents.""" |
| 62 | + |
| 63 | + shadow_invalid = """ |
| 64 | + no_last_change:$6$salt$hash1::0:99999:7::123456: |
| 65 | + no_max_age:$6$salt$hash2:18963:0::7::: |
| 66 | + only_last_change:$6$salt$hash3:18963:::::: |
| 67 | + no_int_fields:$6$salt$hash4:string:::::: |
| 68 | + daemon:*:18474:0:99999:7::: |
| 69 | + bin:*:18474:0:99999:7::: |
| 70 | + nobody:*:18474:0:99999:7::: |
| 71 | + regular:$6$salt$hash5:1337:0:99999:7::123456: |
| 72 | + """ |
| 73 | + fs_unix.map_file_fh("/etc/shadow", BytesIO(dedent(shadow_invalid).encode())) |
| 74 | + |
| 75 | + results = list(target_unix_users.passwords()) |
| 76 | + assert len(results) == 5 |
| 77 | + |
| 78 | + assert [r.name for r in results] == [ |
| 79 | + "no_last_change", |
| 80 | + "no_max_age", |
| 81 | + "only_last_change", |
| 82 | + "no_int_fields", |
| 83 | + "regular", |
| 84 | + ] |
| 85 | + |
| 86 | + assert results[0].name == "no_last_change" |
| 87 | + assert results[0].last_change is None |
| 88 | + assert results[0].min_age is None |
| 89 | + assert results[0].max_age is None |
| 90 | + assert results[0].warning_period == 7 |
| 91 | + assert results[0].inactivity_period is None |
| 92 | + assert results[0].expiration_date == datetime(2308, 1, 6, tzinfo=timezone.utc) |
| 93 | + |
| 94 | + assert results[1].name == "no_max_age" |
| 95 | + assert results[1].last_change == datetime(2021, 12, 2, tzinfo=timezone.utc) |
| 96 | + assert results[1].max_age is None |
| 97 | + |
| 98 | + assert results[2].name == "only_last_change" |
| 99 | + assert results[2].last_change == datetime(2021, 12, 2, tzinfo=timezone.utc) |
| 100 | + |
| 101 | + assert results[3].name == "no_int_fields" |
| 102 | + assert results[3].last_change is None |
| 103 | + assert ( |
| 104 | + "Unable to parse last_change shadow value in /etc/shadow: invalid literal for int() with base 10: 'string' ('string')" |
| 105 | + in caplog.text |
| 106 | + ) |
| 107 | + |
| 108 | + # make sure we parsed the last entry even though the other entries are 'broken' |
| 109 | + assert results[-1].name == "regular" |
| 110 | + assert results[-1].salt == "salt" |
| 111 | + assert results[-1].hash == "hash5" |
| 112 | + assert results[-1].algorithm == "sha512" |
| 113 | + assert results[-1].last_change == datetime(1973, 8, 30, tzinfo=timezone.utc) |
| 114 | + assert results[-1].min_age is None |
| 115 | + assert results[-1].max_age == datetime(2247, 6, 14, tzinfo=timezone.utc) |
| 116 | + assert results[-1].warning_period == 7 |
| 117 | + assert results[-1].inactivity_period is None |
| 118 | + assert results[-1].expiration_date == datetime(2308, 1, 6, tzinfo=timezone.utc) |
0 commit comments