1let
2 password1 = "foobar";
3 password2 = "helloworld";
4 password3 = "bazqux";
5 password4 = "asdf123";
6 hashed_bcrypt = "$2b$05$8xIEflrk2RxQtcVXbGIxs.Vl0x7dF1/JSv3cyX6JJt0npzkTCWvxK"; # fnord
7 hashed_yeshash = "$y$j9T$d8Z4EAf8P1SvM/aDFbxMS0$VnTXMp/Hnc7QdCBEaLTq5ZFOAFo2/PM0/xEAFuOE88."; # fnord
8in import ./make-test-python.nix ({ pkgs, ... }: {
9 name = "shadow";
10 meta = with pkgs.lib.maintainers; { maintainers = [ nequissimus ]; };
11
12 nodes.shadow = { pkgs, ... }: {
13 environment.systemPackages = [ pkgs.shadow ];
14
15 users = {
16 mutableUsers = true;
17 users.emma = {
18 isNormalUser = true;
19 password = password1;
20 shell = pkgs.bash;
21 };
22 users.layla = {
23 isNormalUser = true;
24 password = password2;
25 shell = pkgs.shadow;
26 };
27 users.ash = {
28 isNormalUser = true;
29 password = password4;
30 shell = pkgs.bash;
31 };
32 users.berta = {
33 isNormalUser = true;
34 hashedPassword = hashed_bcrypt;
35 shell = pkgs.bash;
36 };
37 users.yesim = {
38 isNormalUser = true;
39 hashedPassword = hashed_yeshash;
40 shell = pkgs.bash;
41 };
42 };
43 };
44
45 testScript = ''
46 shadow.wait_for_unit("multi-user.target")
47 shadow.wait_until_succeeds("pgrep -f 'agetty.*tty1'")
48
49 with subtest("Normal login"):
50 shadow.send_key("alt-f2")
51 shadow.wait_until_succeeds("[ $(fgconsole) = 2 ]")
52 shadow.wait_for_unit("getty@tty2.service")
53 shadow.wait_until_succeeds("pgrep -f 'agetty.*tty2'")
54 shadow.wait_until_tty_matches("2", "login: ")
55 shadow.send_chars("emma\n")
56 shadow.wait_until_tty_matches("2", "login: emma")
57 shadow.wait_until_succeeds("pgrep login")
58 shadow.sleep(2)
59 shadow.send_chars("${password1}\n")
60 shadow.send_chars("whoami > /tmp/1\n")
61 shadow.wait_for_file("/tmp/1")
62 assert "emma" in shadow.succeed("cat /tmp/1")
63
64 with subtest("Switch user"):
65 shadow.send_chars("su - ash\n")
66 shadow.sleep(2)
67 shadow.send_chars("${password4}\n")
68 shadow.sleep(2)
69 shadow.send_chars("whoami > /tmp/3\n")
70 shadow.wait_for_file("/tmp/3")
71 assert "ash" in shadow.succeed("cat /tmp/3")
72
73 with subtest("Change password"):
74 shadow.send_key("alt-f3")
75 shadow.wait_until_succeeds("[ $(fgconsole) = 3 ]")
76 shadow.wait_for_unit("getty@tty3.service")
77 shadow.wait_until_succeeds("pgrep -f 'agetty.*tty3'")
78 shadow.wait_until_tty_matches("3", "login: ")
79 shadow.send_chars("emma\n")
80 shadow.wait_until_tty_matches("3", "login: emma")
81 shadow.wait_until_succeeds("pgrep login")
82 shadow.sleep(2)
83 shadow.send_chars("${password1}\n")
84 shadow.send_chars("passwd\n")
85 shadow.sleep(2)
86 shadow.send_chars("${password1}\n")
87 shadow.sleep(2)
88 shadow.send_chars("${password3}\n")
89 shadow.sleep(2)
90 shadow.send_chars("${password3}\n")
91 shadow.sleep(2)
92 shadow.send_key("alt-f4")
93 shadow.wait_until_succeeds("[ $(fgconsole) = 4 ]")
94 shadow.wait_for_unit("getty@tty4.service")
95 shadow.wait_until_succeeds("pgrep -f 'agetty.*tty4'")
96 shadow.wait_until_tty_matches("4", "login: ")
97 shadow.send_chars("emma\n")
98 shadow.wait_until_tty_matches("4", "login: emma")
99 shadow.wait_until_succeeds("pgrep login")
100 shadow.sleep(2)
101 shadow.send_chars("${password1}\n")
102 shadow.wait_until_tty_matches("4", "Login incorrect")
103 shadow.wait_until_tty_matches("4", "login:")
104 shadow.send_chars("emma\n")
105 shadow.wait_until_tty_matches("4", "login: emma")
106 shadow.wait_until_succeeds("pgrep login")
107 shadow.sleep(2)
108 shadow.send_chars("${password3}\n")
109 shadow.send_chars("whoami > /tmp/2\n")
110 shadow.wait_for_file("/tmp/2")
111 assert "emma" in shadow.succeed("cat /tmp/2")
112
113 with subtest("Groups"):
114 assert "foobar" not in shadow.succeed("groups emma")
115 shadow.succeed("groupadd foobar")
116 shadow.succeed("usermod -a -G foobar emma")
117 assert "foobar" in shadow.succeed("groups emma")
118
119 with subtest("nologin shell"):
120 shadow.send_key("alt-f5")
121 shadow.wait_until_succeeds("[ $(fgconsole) = 5 ]")
122 shadow.wait_for_unit("getty@tty5.service")
123 shadow.wait_until_succeeds("pgrep -f 'agetty.*tty5'")
124 shadow.wait_until_tty_matches("5", "login: ")
125 shadow.send_chars("layla\n")
126 shadow.wait_until_tty_matches("5", "login: layla")
127 shadow.wait_until_succeeds("pgrep login")
128 shadow.send_chars("${password2}\n")
129 shadow.wait_until_tty_matches("5", "login:")
130
131 with subtest("check alternate password hashes"):
132 shadow.send_key("alt-f6")
133 shadow.wait_until_succeeds("[ $(fgconsole) = 6 ]")
134 for u in ["berta", "yesim"]:
135 shadow.wait_for_unit("getty@tty6.service")
136 shadow.wait_until_succeeds("pgrep -f 'agetty.*tty6'")
137 shadow.wait_until_tty_matches("6", "login: ")
138 shadow.send_chars(f"{u}\n")
139 shadow.wait_until_tty_matches("6", f"login: {u}")
140 shadow.wait_until_succeeds("pgrep login")
141 shadow.sleep(2)
142 shadow.send_chars("fnord\n")
143 shadow.send_chars(f"whoami > /tmp/{u}\n")
144 shadow.wait_for_file(f"/tmp/{u}")
145 print(shadow.succeed(f"cat /tmp/{u}"))
146 assert u in shadow.succeed(f"cat /tmp/{u}")
147 shadow.send_chars("logout\n")
148 '';
149})